From cc2b707459c5cdd1bd792346ffc2f10574bef9ad Mon Sep 17 00:00:00 2001 From: Durable Workflow Date: Fri, 9 Oct 2026 02:02:09 +0000 Subject: [PATCH 1/2] docs: start Brazilian Portuguese onboarding and navigation --- docusaurus.config.js | 3 +- i18n/pt-BR/code.json | 32 ++++ .../current.json | 25 +++ .../current/installation.md | 65 ++++++++ .../current/introduction.md | 150 ++++++++++++++++++ .../docusaurus-theme-classic/footer.json | 10 ++ .../docusaurus-theme-classic/navbar.json | 6 + 7 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 i18n/pt-BR/code.json create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current.json create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/installation.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md create mode 100644 i18n/pt-BR/docusaurus-theme-classic/footer.json create mode 100644 i18n/pt-BR/docusaurus-theme-classic/navbar.json diff --git a/docusaurus.config.js b/docusaurus.config.js index dcecec3fc6..33f921dd15 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -53,11 +53,12 @@ const config = { i18n: { defaultLocale: 'en', - locales: ['en', 'uk', 'es'], + locales: ['en', 'uk', 'es', 'pt-BR'], localeConfigs: { en: {label: 'English', htmlLang: 'en'}, uk: {label: 'Українська', htmlLang: 'uk'}, es: {label: 'Español', htmlLang: 'es'}, + 'pt-BR': {label: 'Português (Brasil)', htmlLang: 'pt-BR'}, }, }, diff --git a/i18n/pt-BR/code.json b/i18n/pt-BR/code.json new file mode 100644 index 0000000000..165dd71c74 --- /dev/null +++ b/i18n/pt-BR/code.json @@ -0,0 +1,32 @@ +{ + "theme.SearchPage.existingResultsTitle": {"message": "Resultados da busca por «{query}»"}, + "theme.SearchPage.emptyResultsTitle": {"message": "Buscar na documentação"}, + "theme.SearchPage.searchContext.everywhere": {"message": "Em toda a documentação"}, + "theme.SearchPage.documentsFound.plurals": {"message": "{count} documento encontrado|{count} documentos encontrados"}, + "theme.SearchPage.noResultsText": {"message": "Nenhum documento encontrado"}, + "theme.SearchBar.noResultsText": {"message": "Nenhum resultado"}, + "theme.SearchBar.seeAllOutsideContext": {"message": "Ver todos os resultados fora de «{context}»"}, + "theme.SearchBar.searchInContext": {"message": "Ver todos os resultados em «{context}»"}, + "theme.SearchBar.seeAll": {"message": "Ver todos os resultados"}, + "theme.SearchBar.label": {"message": "Buscar"}, + "docs.translationFallback": {"message": "Esta página está disponível em inglês."}, + "docs.readInEnglish": {"message": "Ler em inglês"}, + "homepage.tagline": {"message": "Execução durável para PHP, Python e Rust."}, + "homepage.getStarted": {"message": "Comece aqui"}, + "homepage.deploymentModes": {"message": "Escolha um modelo de implantação"}, + "homepage.description": {"message": "Plataforma de execução durável para PHP, Python e Rust, disponível como Cloud gerenciado, Server sob sua administração ou integrada ao Laravel."}, + "homepage.cloudDescription": {"message": "Execute workers de PHP, Python ou Rust em um namespace gerenciado enquanto o Durable Workflow opera o ambiente de orquestração."}, + "homepage.features.languages.title": {"message": "Desenvolva com PHP, Python ou Rust"}, + "homepage.features.languages.description": {"message": "Crie clientes, workflows e atividades com os SDKs oficiais, que compartilham um protocolo público e um formato de dados Avro portável."}, + "homepage.features.runtime.title": {"message": "Escolha quem opera o ambiente"}, + "homepage.features.runtime.description": {"message": "Use o Durable Workflow Cloud, execute um Server independente ou mantenha o ambiente integrado à sua aplicação Laravel."}, + "homepage.features.recovery.title": {"message": "Retome o trabalho após uma falha"}, + "homepage.features.recovery.description": {"message": "Histórico durável, replay determinístico, temporizadores, novas tentativas, sinais, atualizações, workflows filhos e sagas permitem dar continuidade a processos longos."}, + "promotion.eyebrow": {"message": "Serviço gerenciado · grupo limitado"}, + "promotion.title": {"message": "Grupo de lançamento do Durable Workflow Cloud"}, + "promotion.requestAccess": {"message": "Solicite acesso antecipado"}, + "navbar.github.repository": {"message": "Repositório no GitHub"}, + "navbar.github.repositoryStars": {"message": "{repository} ({count} estrelas)"}, + "navbar.github.starCount": {"message": "Estrelas no GitHub: {count}"}, + "navbar.github.stars": {"message": "Estrelas no GitHub"} +} diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current.json b/i18n/pt-BR/docusaurus-plugin-content-docs/current.json new file mode 100644 index 0000000000..61ef18db70 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,25 @@ +{ + "version.label": {"message": "2.0"}, + "sidebar.tutorialSidebar.category.Service Mode": {"message": "Modo de serviço"}, + "sidebar.tutorialSidebar.category.Service Mode.link.generated-index.description": {"message": "Conecte clientes e workers oficiais de PHP, Python ou Rust ao Cloud ou a um Server sob sua administração. O Cloud inclui o Managed Waterline. Para Server, o Waterline é implantado separadamente."}, + "sidebar.tutorialSidebar.category.CLI": {"message": "CLI"}, + "sidebar.tutorialSidebar.category.CLI.link.generated-index.description": {"message": "Instale o cliente de operação dw e consulte seus comandos."}, + "sidebar.tutorialSidebar.category.SDKs": {"message": "SDKs"}, + "sidebar.tutorialSidebar.category.SDKs.link.generated-index.description": {"message": "Crie clientes e workers no modo de serviço com os SDKs oficiais de PHP, Python e Rust."}, + "sidebar.tutorialSidebar.category.Embedded": {"message": "Modo integrado"}, + "sidebar.tutorialSidebar.category.Embedded.link.generated-index.description": {"message": "Instale, desenvolva, teste e configure workflows em uma aplicação Laravel."}, + "sidebar.tutorialSidebar.category.Defining Workflows": {"message": "Definição de workflows"}, + "sidebar.tutorialSidebar.category.Defining Workflows.link.generated-index.description": {"message": "Defina workflows, atividades, dados, identificadores e estados no Laravel integrado."}, + "sidebar.tutorialSidebar.category.Features": {"message": "Recursos"}, + "sidebar.tutorialSidebar.category.Features.link.generated-index.description": {"message": "Escolha mensagens, comandos duráveis, esperas, novas tentativas, versões e limites de histórico no Laravel integrado."}, + "sidebar.tutorialSidebar.category.Constraints": {"message": "Restrições"}, + "sidebar.tutorialSidebar.category.Constraints.link.generated-index.description": {"message": "Conheça as regras de determinismo e os limites estruturais do modo integrado."}, + "sidebar.tutorialSidebar.category.Configuration": {"message": "Configuração"}, + "sidebar.tutorialSidebar.category.Configuration.link.generated-index.description": {"message": "Configure armazenamento, conexões, limites e retenção no Laravel integrado."}, + "sidebar.tutorialSidebar.category.Run And Operate": {"message": "Execução e operação"}, + "sidebar.tutorialSidebar.category.Run And Operate.link.generated-index.description": {"message": "Implante, observe, diagnostique, recupere e mantenha seus workflows."}, + "sidebar.tutorialSidebar.category.External Execution And Ingress": {"message": "Execução externa e entrada de solicitações"}, + "sidebar.tutorialSidebar.category.External Execution And Ingress.link.generated-index.description": {"message": "Conecte APIs HTTP, workers externos, filas e outras integrações de protocolo."}, + "sidebar.tutorialSidebar.category.AI And Automation": {"message": "IA e automação"}, + "sidebar.tutorialSidebar.category.AI And Automation.link.generated-index.description": {"message": "Forneça documentação, manifestos, comandos e ferramentas estáveis para agentes e automação."} +} diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/installation.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/installation.md new file mode 100644 index 0000000000..048fd97e3f --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/installation.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 3 +--- + +# Instalação {#installation} + +Este guia mostra como instalar o pacote PHP Durable Workflow em uma aplicação +Laravel. + +Se você ainda está escolhendo entre o modo integrado e o Server independente, +comece por [Modelos de implantação](/docs/polyglot/deployment-modes). Esta página +trata do Laravel integrado. + +> **Prefere começar com uma aplicação pronta?** O +> [Sample App](/docs/sample-app) é um projeto Laravel 13 executável com +> Durable Workflow 2.0. Ele inclui um workflow por tipo de padrão e caminhos +> para Codespaces e `docker compose`. Clone o projeto e execute +> `php artisan app:init` para obter a instalação pronta. Volte a este guia +> quando quiser adicionar Durable Workflow à sua própria aplicação Laravel. + +## Requisitos {#requirements} + +- PHP 8.1 ou superior +- Laravel 9 ou superior + +O Durable Workflow pode usar qualquer driver de fila compatível com Laravel, +exceto `sync`, incluindo: + +- Amazon SQS +- Beanstalkd +- Database +- Redis + +Cada driver de fila tem seus próprios +[pré-requisitos](https://laravel.com/docs/12.x/queues#driver-prerequisites). + +O Durable Workflow também exige um driver de cache com suporte a +[locks](https://laravel.com/docs/12.x/cache#atomic-locks). + +## Instale o Durable Workflow {#installing-durable-workflow} + +Instale o pacote pelo Composer: + +```bash +composer require %%artifact.workflowComposerPackage%% +``` + +Use `durable-workflow/workflow:^2.0` para aceitar automaticamente atualizações +compatíveis de 2.x. Consulte [Compatibilidade de versões](/docs/compatibility) +para conhecer as regras do ambiente e dos pacotes. + +O pacote carrega suas migrations automaticamente. Após a instalação, basta +executar a migração normal: + +```bash +php artisan migrate +``` + +## Execute os workers {#running-workers} + +O Durable Workflow usa filas para executar workflows e atividades em segundo +plano. Execute o [comando `queue:work`](https://laravel.com/docs/12.x/queues#the-queue-work-command) +ou use o [Horizon](https://laravel.com/docs/12.x/horizon) para operar os workers +de fila. Sem um worker, workflows e atividades não são processados. Para +executá-los em paralelo, você precisa de mais de um worker de fila. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md new file mode 100644 index 0000000000..f31c4c1f4f --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md @@ -0,0 +1,150 @@ +--- +sidebar_position: 1 +description: Escolha Durable Workflow Cloud, um Server sob sua administração ou Laravel integrado e conclua seu primeiro workflow com um SDK oficial. +tags: + - concepts + - getting-started + - workflows +keywords: + - durable workflow + - execução durável + - orquestração durável + - workflows em vários linguagens +--- + +import PythonPackageReleaseLink from '@site/src/components/PythonPackageReleaseLink'; + +# Introdução {#introduction} + +O Durable Workflow 2.0 mantém o estado e o histórico dos workflows fora dos +processos de curta duração da aplicação. Assim, workers de PHP, Python e Rust +podem retomar o trabalho com segurança após um reinício. Comece pelo +[Guia de início rápido](/docs/quickstart/) para levar um workflow ao estado +`completed` antes de consultar o [Índice de capacidades](/docs/capabilities/). + +## Escolha um modelo de implantação {#choose-a-deployment-model} + +### Modo de serviço {#service-mode} + +As aplicações acessam um ambiente de execução durável remoto pelos SDKs +oficiais. Escolha quem opera esse ambiente: + +- **[Durable Workflow Cloud](/docs/polyglot/cloud-control-plane/)** é a opção + gerenciada. O Durable Workflow opera a orquestração, a persistência e o + Managed Waterline. Sua equipe executa os clientes e workers do SDK no + namespace provisionado. **Usuários do Cloud não instalam nem executam + Durable Workflow Server ou um serviço Waterline separado.** +- **[Server sob sua administração](/docs/polyglot/server/)** oferece a mesma + interface de serviço. Sua equipe implanta, protege, dimensiona, faz backups + e atualiza o ambiente. O Waterline é um serviço de observação separado que + você pode implantar para acessar o namespace administrado pelo Server. + +As duas opções compartilham o plano de controle HTTP+JSON versionado, o +protocolo de workers, o modelo de namespaces e o envelope de dados independente +da linguagem. Consulte [Modelos de implantação](/docs/polyglot/deployment-modes/) +para entender as responsabilidades de cada modelo. + +### Laravel integrado {#embedded-laravel} + +O modo integrado é um modelo de implantação para aplicações Laravel que +mantêm o estado dos workflows, as filas, a configuração e as ferramentas de +operação na própria infraestrutura. Ele instala `durable-workflow/workflow`, +sem se conectar ao Cloud ou exigir um Server separado. O pacote Waterline +integrado lê o estado da aplicação no mesmo processo. + +Siga a [Instalação integrada](/docs/installation/) quando quiser que a aplicação +seja responsável por esse ambiente. + +Equipes Laravel que vêm da versão estável v1 ou estão reconsiderando uma +implantação integrada de 2.0 podem consultar o +[Guia de adoção e transição de ambiente no Laravel](/docs/laravel-adoption/) +para comparar os caminhos executáveis do modo integrado e do SDK PHP antes +de alterar o tráfego. + +## Escolha um SDK para o modo de serviço {#choose-a-service-mode-sdk} + +- **[SDK PHP](/docs/polyglot/php/):** instale `durable-workflow/sdk` em uma + aplicação PHP independente de framework ou em um worker remoto. +- **[SDK Python](/docs/polyglot/python/):** escreva workflows determinísticos + e atividades e use o cliente assíncrono do plano de controle. A + versão estável do + Python consta no mesmo manifesto de versões + estáveis usado pelo guia de início rápido do Server. +- **[SDK Rust](/docs/polyglot/rust/):** escreva workflows determinísticos e + atividades e execute serviços de workers nativos. + +Os três são implementações oficiais da mesma interface pública. O guia de +[Capacidades de clientes e workers](/docs/polyglot/cli-python-parity/) +explica os recursos disponíveis e as diferenças entre as interfaces. + +## Seu primeiro workflow concluído {#your-first-completed-workflow} + +O [Guia de início rápido](/docs/quickstart/) apresenta o objetivo, a escolha do +ambiente, os requisitos, o tempo estimado e o resultado esperado. Você pode +seguir um caminho em PHP, Python ou Rust, um de cada vez. O caminho local usa +artefatos publicados sem exigir o código-fonte do produto. O caminho do Cloud +usa os dados de conexão do namespace gerenciado, sem executar Server. + +## Como as partes do modo de serviço se conectam {#how-service-mode-fits-together} + +Uma implantação no modo de serviço tem três partes: + +- **O ambiente de execução** mantém o estado durável, os comandos e o + histórico, a atribuição de tarefas, os temporizadores, os agendamentos, os + namespaces e os protocolos autenticados. O Cloud opera os namespaces + gerenciados. Sua equipe opera o ambiente quando administra o Server. +- **Os workers da aplicação** executam workflows e atividades pelos SDKs de + PHP, Python ou Rust. Eles podem ser implantados junto com a aplicação ou + como serviços independentes e escalar separadamente do ambiente. +- **Os clientes e as ferramentas de operação** iniciam, inspecionam e + controlam o mesmo estado pelos SDKs, pelo CLI `dw`, pelas APIs HTTP, pelos + esquemas legíveis por máquina, pelo Waterline e pelas interfaces para agentes. + +## Um contrato público de execução durável {#one-public-durable-execution-contract} + +Os SDKs oficiais compartilham nomes de tipos de workflow e atividade +registrados como strings e um envelope público de dados. O envelope identifica +seu codec e contém valores portáveis, em vez de serialização PHP, pickles do +Python ou tipos internos do Rust. + +Os workers de workflow reconstroem as decisões a partir dos comandos e do +histórico durável. Entradas e resultados de atividades e workflows filhos podem +cruzar linguagens quando os workers anunciam o mesmo codec público e registram +os mesmos nomes de tipos. Consulte o [Índice de capacidades](/docs/capabilities/) +e as informações do ambiente antes de depender de uma interface específica de +um SDK. + +## Aprenda com os exemplos do seu modelo {#learn-from-the-matching-examples} + +- **Modo de serviço e várias linguagens:** siga o + [Guia de início rápido](/docs/quickstart/) e o guia do SDK escolhido. +- **Laravel integrado:** explore os padrões nativos do Laravel e as + informações do Waterline na galeria [Sample App](/docs/sample-app/). + +Para Cloud ou Server sob sua administração, comece pelo guia do modo de +serviço. A galeria integrada é voltada ao Laravel. + +## Operação por agentes com um contrato explícito {#agent-operable-by-contract} + +Pessoas e agentes autônomos usam o mesmo contrato legível por máquina. +O ciclo verificável é **Descobrir → Alterar → Executar → Diagnosticar → Reparar**: +manifestos de versões e capacidades, comandos explícitos, resultados +estruturados, histórico tipado, diagnósticos de workers e filas, alterações +seguras e verificação posterior. Consulte o +[Ciclo de operação de agentes](/docs/agent-operating-loop/) e o +[Avaliador de motores para agentes de IA](/docs/ai-agent-workflow-engine/). + +## Você precisa de um workflow? {#do-you-need-a-workflow} + +Provavelmente precisa se: + +- O processo dura minutos, horas ou dias. +- Você precisa aguardar uma aprovação humana. +- Você precisa aguardar um webhook ou outro evento externo. +- Você quer pausar e continuar depois sem manter um processo em execução. +- Você precisa retomar após uma falha sem causar erros ou duplicar trabalho. + +Se a tarefa é executar cinco jobs em ordem e parar na primeira falha, uma cadeia +de jobs costuma ser mais adequada. O Durable Workflow atende a casos em que +a próxima etapa depende de um evento externo, de uma espera ou de uma decisão +que não pode ser conhecida antecipadamente. diff --git a/i18n/pt-BR/docusaurus-theme-classic/footer.json b/i18n/pt-BR/docusaurus-theme-classic/footer.json new file mode 100644 index 0000000000..33499292e3 --- /dev/null +++ b/i18n/pt-BR/docusaurus-theme-classic/footer.json @@ -0,0 +1,10 @@ +{ + "link.title.Docs": {"message": "Documentação"}, + "link.title.Community": {"message": "Comunidade"}, + "link.title.More": {"message": "Mais recursos"}, + "link.item.label.Introduction": {"message": "Introdução"}, + "link.item.label.Installation": {"message": "Instalação"}, + "link.item.label.Durable Workflow vs Temporal": {"message": "Durable Workflow e Temporal"}, + "link.item.label.LLM Docs": {"message": "Documentação para LLMs"}, + "copyright": {"message": "Copyright © 2026 Durable Workflow."} +} diff --git a/i18n/pt-BR/docusaurus-theme-classic/navbar.json b/i18n/pt-BR/docusaurus-theme-classic/navbar.json new file mode 100644 index 0000000000..d28ce81867 --- /dev/null +++ b/i18n/pt-BR/docusaurus-theme-classic/navbar.json @@ -0,0 +1,6 @@ +{ + "logo.alt": {"message": "Logotipo do Durable Workflow"}, + "item.label.Docs": {"message": "Documentação"}, + "item.label.Blog": {"message": "Blog (em inglês)"}, + "item.label.Star on GitHub": {"message": "Apoie o projeto no GitHub"} +} From 466456457628ecb46a1cdb10e4bbe0bffc51dc6f Mon Sep 17 00:00:00 2001 From: Durable Workflow Date: Fri, 9 Oct 2026 02:18:48 +0000 Subject: [PATCH 2/2] docs: complete Portuguese onboarding and core guides --- README.md | 26 +- docs/how-it-works.md | 6 +- .../current/how-it-works.md | 4 +- i18n/es/source-hashes.json | 2 +- .../constraints/activity-constraints.md | 44 ++ .../constraints/execution-guarantees.md | 151 ++++++ .../current/constraints/overview.md | 21 + .../constraints/workflow-constraints.md | 41 ++ .../current/defining-workflows/workflows.md | 38 ++ .../current/failures-and-recovery.md | 305 +++++++++++ .../current/how-it-works.md | 109 ++++ .../current/introduction.md | 4 +- .../current/polyglot/deployment-modes.md | 160 ++++++ .../current/quickstart.md | 493 ++++++++++++++++++ i18n/pt-BR/source-hashes.json | 13 + .../current/how-it-works.md | 6 +- scripts/check-doc-translations.js | 59 ++- 17 files changed, 1435 insertions(+), 47 deletions(-) create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/activity-constraints.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/execution-guarantees.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/overview.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/workflow-constraints.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/defining-workflows/workflows.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/failures-and-recovery.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/how-it-works.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/polyglot/deployment-modes.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/quickstart.md create mode 100644 i18n/pt-BR/source-hashes.json diff --git a/README.md b/README.md index d6b987b6b7..10b4e70773 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ npm run build ## Translations English is the default locale. Ukrainian is available under `/uk/`. Spanish -onboarding and core guides are available under `/es/`. Untranslated reference +onboarding and core guides are available under `/es/`, and Brazilian Portuguese +under `/pt-BR/`. Untranslated reference pages show the English text with a language notice and a link to the English route. Blog articles and versioned 1.x documentation remain in English. @@ -36,6 +37,7 @@ Preview one locale with: ```bash npm run start -- --locale uk npm run start -- --locale es +npm run start -- --locale pt-BR ``` Translate current docs in `i18n//docusaurus-plugin-content-docs/current/` @@ -44,7 +46,9 @@ English source file paths, examples, commands, API names and explicit heading anchors intact. Ukrainian keeps workflow, activity, worker, signal, timer, query, update and saga as English technical terms. Spanish uses workflow and worker, with actividad, señal, temporizador, consulta and actualización in -prose. Never translate identifiers in code, protocol fields or type names. +prose. Brazilian Portuguese keeps workflow, worker, namespace and replay, with +atividade, sinal, temporizador, consulta and atualização in prose. Never +translate identifiers in code, protocol fields or type names. Use Docusaurus translation markers for component text. Run `npm run write-translations -- --locale uk` to extract new messages, then @@ -54,18 +58,21 @@ all locales with `npm run build` before submitting a change. When changing an English guide that already has a translation, review and update that translation in the same pull request. Review the prose for fluent language and the technical contract for accuracy. Keep executable code blocks -identical to the English source. The build checks this for Spanish. If a +identical to the English source. The build checks this for Spanish and +Brazilian Portuguese. If a translation cannot be brought current, remove the stale translated file so readers see the current English source and the fallback notice. Artifact version placeholders stay shared with English and update automatically. The documentation maintainers own translation upkeep. The existing build also -compares reviewed English source hashes for Spanish and reports the guides +compares reviewed English source hashes for Spanish and Brazilian Portuguese +and reports the guides needing review. Prose edits produce a notice, while changed executable examples must still match. After reviewing and updating the translation, run `node scripts/check-doc-translations.js --record-review` -and commit `i18n/es/source-hashes.json` with it. This command still checks the -code blocks. Recording hashes alone does not review the prose. +and commit the affected `i18n//source-hashes.json` files with it. +This command still checks the code blocks. Recording hashes alone does not +review the prose. The pinned Docusaurus 3.10.2 utility patch resolves relative Markdown links between translated and English fallback files. It uses Docusaurus's document @@ -76,9 +83,10 @@ Docusaurus if the upstream resolver handles these cases. For a new locale, qualify installation, a completed first workflow, core concepts, safe recovery, navigation, search, language switching and fallback before publication. Check representative pages in a browser, including mobile, -and verify canonical and alternate-language links. Spanish is the first -expansion in [#169](https://github.com/durable-workflow/durable-workflow.github.io/issues/169). -Portuguese (Brazil), Simplified Chinese and Japanese remain subsequent stages, +and verify canonical and alternate-language links. Spanish and Brazilian +Portuguese are the first expansions in +[#169](https://github.com/durable-workflow/durable-workflow.github.io/issues/169). +Simplified Chinese and Japanese remain subsequent stages, subject to audience evidence and language review. Country totals alone do not establish a reader's preferred language. diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 43042e5f6f..32b0f4f6c1 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -28,7 +28,7 @@ User workflow code lives in `handle()`, which is an ordinary method that calls t By calling multiple activities, a workflow can orchestrate the results between each of them. The execution of the workflow and the durable steps it schedules are interleaved: the workflow reaches an activity call, suspends until that activity completes, and then continues execution from where it left off. -If a workflow fails, the events leading up to the failure are replayed to rebuild the current state. This allows the workflow to pick up where it left off, with the same inputs and outputs as before, ensuring determinism. +If a workflow worker crashes, committed events are replayed to rebuild the current state. This allows the workflow to pick up where it left off, with the same inputs and outputs as before, ensuring determinism. An unhandled workflow failure makes the run terminal; replay does not retry a failed run. Ordinary activities are durable queued work in v2. Explicit [local activities](./features/local-activities.md) run short activity work in @@ -69,7 +69,7 @@ class MyWorkflow extends Workflow return [ activity(TestActivity::class), activity(TestOtherActivity::class), - fn () => all([ + all([ fn () => activity(TestParallelActivity::class), fn () => activity(TestParallelOtherActivity::class), ]), @@ -97,7 +97,7 @@ import ThemedImage from '@site/src/components/ThemedImage'; 3. At this point, the workflow enters the event sourcing replay loop. This is where it goes back to the database and looks at the event stream to rebuild the current state. This is necessary because the workflow is not a long running process. The workflow exits while any activities are running and then is dispatched again after completion. 4. Once the event stream has been replayed, the workflow continues to the next activity, `TestOtherActivity`, and starts it by dispatching it as a queued job. Again, once `TestOtherActivity` has completed, it saves the result to the database and returns control to the workflow by dispatching it as a queued job. 5. The workflow then enters the event sourcing replay loop again, rebuilding the current state from the event stream. -6. Next, the workflow starts two parallel activities, `TestParallelActivity` and `TestOtherParallelActivity`. Both activities are dispatched. Once they have completed, they save the results to the database and return control to the workflow. +6. Next, the workflow starts two parallel activities, `TestParallelActivity` and `TestParallelOtherActivity`. Both activities are dispatched. Once they have completed, they save the results to the database and return control to the workflow. 7. Finally, the workflow enters the event sourcing replay loop one last time to rebuild the current state from the event stream. This completes the execution of the workflow. ## Determinism diff --git a/i18n/es/docusaurus-plugin-content-docs/current/how-it-works.md b/i18n/es/docusaurus-plugin-content-docs/current/how-it-works.md index 0c7175312d..f0664138a9 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/how-it-works.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/how-it-works.md @@ -50,6 +50,8 @@ de actividad, se suspende hasta recibir su resultado y continúa desde ese punto Para recuperar una tarea tras un fallo, el motor reproduce los eventos confirmados y reconstruye el estado con las mismas entradas y salidas. +Un fallo no controlado del workflow deja el run en un estado terminal. +El replay no vuelve a intentar un run fallido. En v2, las actividades ordinarias son tareas duraderas en cola y pueden ejecutarse en cualquier worker compatible. Las @@ -100,7 +102,7 @@ class MyWorkflow extends Workflow return [ activity(TestActivity::class), activity(TestOtherActivity::class), - fn () => all([ + all([ fn () => activity(TestParallelActivity::class), fn () => activity(TestParallelOtherActivity::class), ]), diff --git a/i18n/es/source-hashes.json b/i18n/es/source-hashes.json index 205ce704ec..b9b4cf1a0f 100644 --- a/i18n/es/source-hashes.json +++ b/i18n/es/source-hashes.json @@ -5,7 +5,7 @@ "constraints/workflow-constraints.md": "a69c8a118dc790a5d7b6c91806cb7ceca3015004a0fe549aee3094b09cbf17ad", "defining-workflows/workflows.md": "a0cc26ae208ad3cee612f49e8db41324ee18f700fe67d9f533e48c700df5c386", "failures-and-recovery.md": "0d2b8c71b7b54a0160c9bd631348a8223c0d05749f7224ec3ea5feaf74a0a3b5", - "how-it-works.md": "119a8aa26802a02aca05e94b80558a4b19b9ac86524ddec0d25416ea24ced688", + "how-it-works.md": "5233792ac55aaf1bd80e3cad88fc3ad23e1eda149255611e257459dd918c52e9", "installation.md": "1e2e04a8f23414d0e81c72b166700fd6350129ab81b2f22a6205856c23741967", "introduction.md": "06bf96d164d402d4e376c6308b20f96d324110b2e0cf96c2a1a67b39906072e3", "polyglot/deployment-modes.md": "ee38a2ca890e6ba5d52fffa1fa0bc65e64a1fc82e47af92463983e523d4e3f49", diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/activity-constraints.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/activity-constraints.md new file mode 100644 index 0000000000..b158e95133 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/activity-constraints.md @@ -0,0 +1,44 @@ +--- +sidebar_position: 3 +title: Restrições das atividades +description: Torne os efeitos externos seguros para novas tentativas, expiração de leases e reentrega. +--- + +# Restrições das atividades {#activity-constraints} + +As atividades atravessam a fronteira durável para realizar operações de entrada e saída e efeitos externos. Elas não passam por replay como o código do workflow. São **executadas pelo menos uma vez**, o que significa que novas tentativas, expiração de leases e reentrega podem fazer com que o mesmo trabalho lógico seja observado mais de uma vez. + +Esse comportamento faz parte do contrato. Se uma atividade faz uma cobrança, envia um e-mail, grava em outro sistema ou modifica um recurso externo, a operação precisa ser segura para repetição. + +## Na prática {#what-this-means-in-practice} + +- O identificador padrão para garantir a idempotência de uma execução lógica de atividade é `activity_execution_id`. +- Cada tentativa também recebe seu próprio `activity_attempt_id`. +- Um worker pode concluir uma operação externa, perder seu lease e enviar o resultado depois. O mecanismo pode rejeitar esse relato tardio porque outro worker já venceu a disputa pelo resultado durável, mas o efeito externo pode ter ocorrido. +- O código da atividade pode realizar operações de entrada e saída, consultar o relógio do sistema e usar estado mutável do processo. O código do workflow não pode. Mantenha essa fronteira clara. + +## Padrões de idempotência recomendados {#preferred-idempotency-patterns} + +Muitas APIs externas aceitam um `Idempotency-Key`. Quando o serviço remoto oferecer esse recurso, use a identidade lógica da atividade fornecida pelo runtime. + +- Prefira `activity_execution_id` quando o sistema remoto deve tratar novas tentativas como a mesma solicitação lógica. +- Use `activity_attempt_id` apenas quando o sistema remoto precisa distinguir as tentativas de um mesmo trabalho lógico. + +Outros padrões úteis: + +- Grave em um recurso externo com nome determinístico ou chave natural. +- Use upserts ou tabelas de deduplicação com um identificador durável como chave. +- Torne a operação naturalmente idempotente, para que a segunda chamada não faça nenhuma alteração. + +Muitas operações já são naturalmente idempotentes. Codificar o mesmo vídeo duas vezes ainda produz o mesmo vídeo. Excluir o mesmo arquivo duas vezes não faz nada na segunda chamada. + +Algumas operações não são naturalmente idempotentes, mas a duplicação ainda pode ser a falha menos prejudicial. Se você não sabe se um e-mail saiu do provedor, enviar uma cópia pode ser preferível a perder a notificação silenciosamente. Faça essa escolha de forma consciente. + +## O que não presumir {#what-not-to-assume} + +- Não presuma que uma tentativa de atividade só executa em um worker. +- Não presuma que uma nova tentativa significa que o efeito externo anterior falhou. +- Não presuma que um relato tardio de conclusão significa que a atividade nunca executou. +- Não mova efeitos externos para o código do workflow para evitar novas tentativas. Isso transforma um problema de idempotência em uma falha de determinismo. + +Veja [Garantias de execução e idempotência](./execution-guarantees.md) para o contrato completo e [Falhas e recuperação](../failures-and-recovery.md) para o modelo de recuperação disponível ao operador. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/execution-guarantees.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/execution-guarantees.md new file mode 100644 index 0000000000..807375d6c4 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/execution-guarantees.md @@ -0,0 +1,151 @@ +--- +sidebar_position: 3.5 +title: Garantias de execução e idempotência +description: O contrato público da v2 para replay, novas tentativas, expiração de leases, reentrega e identificadores de idempotência. +tags: + - constraints + - idempotency + - retries + - replay +keywords: + - garantias de execução + - idempotência + - replay + - reentrega + - expiração de lease + - pelo menos uma vez + - exatamente uma vez +--- + +# Garantias de execução e idempotência {#execution-guarantees-and-idempotency} + +Durable Workflow v2 estabelece uma fronteira clara entre o **replay do workflow** e a **execução de atividades**: + +- O código do workflow passa por replay a partir do histórico confirmado e deve ser determinístico. +- O código da atividade realiza efeitos externos e executa **pelo menos uma vez**. +- A camada de histórico durável registra os resultados confirmados de workflows e atividades exatamente uma vez para um mesmo identificador durável, mesmo quando o transporte entrega o trabalho mais de uma vez. + +Essas garantias permitem sobreviver a reinicializações de workers, expiração de leases, reentrega de filas e implantações graduais sem perder o ponto da execução. + +## Replay não é uma nova tentativa {#replay-is-not-retry} + +As tarefas de workflow reconstroem o estado por replay do histórico confirmado e então decidem o próximo passo. O replay executa novamente o corpo do workflow, mas **não** executa novamente atividades, reenvia sinais nem repete efeitos já registrados no histórico. + +Por isso, o código de orquestração deve ser determinístico. Use recursos seguros para workflows, como [`Workflow::now()`](../defining-workflows/workflow-api.md), [`sideEffect(...)`](../features/side-effects.md), consultas, atualizações, resultados de atividades, memos e atributos de busca quando precisar atravessar a fronteira durável. + +## Atividades executam pelo menos uma vez {#activity-execution-is-at-least-once} + +Como as atividades realizam efeitos externos, seu contrato é diferente: + +- Uma tentativa de atividade pode ser adquirida mais de uma vez. +- A expiração de um lease pode provocar a reentrega a outro worker. +- Um worker pode concluir uma operação externa, perder seu lease e enviar o resultado depois. +- Uma nova tentativa agenda outra tentativa durável para a mesma execução lógica de atividade. + +Observar o mesmo trabalho mais de uma vez **faz parte do contrato**. Quem desenvolve a aplicação deve tornar o corpo da atividade, ou o sistema remoto que ela chama, seguro para repetição. + +Veja [Restrições das atividades](./activity-constraints.md) para orientações de código e [Falhas e recuperação](../failures-and-recovery.md) para o comportamento de recuperação disponível ao operador. + +## O que ocorre exatamente uma vez {#what-is-exactly-once} + +Durable Workflow **não** promete que um processo de worker receba trabalho com efeitos externos apenas uma vez. Ele garante que os fatos duráveis confirmados são a fonte de verdade e não se duplicam para um mesmo identificador durável. + +Na prática: + +- Uma decisão confirmada do workflow é persistida uma vez no histórico tipado para o identificador durável de comando ou etapa que representa. +- Um resultado terminal confirmado de uma tentativa de atividade é persistido uma vez para seu `activity_attempt_id`. +- O replay lê esses fatos confirmados e reconstrói o estado do workflow, sem repetir o trabalho externo. + +O modelo central é: + +- **Transporte e workers operam pelo menos uma vez.** +- **O histórico durável confirmado registra os fatos exatamente uma vez por identificador durável.** + +## Expiração de leases e reentrega {#lease-expiry-and-redelivery} + +A expiração de um lease é um caminho normal de recuperação em sistemas distribuídos: + +- Uma tarefa adquirida tem um proprietário de lease e um horário de expiração. +- Se o lease expirar antes de o worker relatar progresso ou conclusão, a tarefa fica disponível para reentrega. +- Outro worker pode adquirir o mesmo trabalho lógico. + +A reentrega não significa que o mecanismo esqueceu o que já foi confirmado. Ela recupera a execução diante da incerteza na camada de worker ou transporte. + +Ao investigar sintomas de execução duplicada, separe duas perguntas: + +1. O efeito externo aconteceu mais de uma vez? +2. O estado durável registrou mais de um resultado confirmado para o mesmo identificador durável? + +A primeira questão exige atividades idempotentes. A segunda faz parte do contrato do mecanismo. + +## Identificadores padrão de idempotência {#default-idempotency-surfaces} + +Estes identificadores são referências estáveis para deduplicar trabalho: + +| Identificador | O que identifica | Uso comum | +| --- | --- | --- | +| `workflow_instance_id` | Uma instância pública de workflow | Tratar inícios duplicados e identificar a execução no domínio da aplicação | +| `workflow_run_id` | Uma execução durável específica | Fixar a execução selecionada para consultas, exportação ou diagnóstico | +| `workflow_command_id` | Um comando externo que modifica estado | Deduplicar novas tentativas de solicitações do cliente | +| `activity_execution_id` | Uma execução lógica de atividade ao longo de novas tentativas | Chave padrão de idempotência para efeitos externos | +| `activity_attempt_id` | Uma tentativa específica dessa atividade | Correlacionar tentativas quando o sistema remoto precisa distingui-las | +| `schedule_id` | Uma definição de agendamento | Deduplicar operações de propriedade e identidade de disparos | +| `idempotencyKey` do fluxo de mensagens | Um envio lógico de mensagem sujeito a novas tentativas | Evitar a ingestão duplicada quando o remetente tenta novamente | + +Na dúvida, use `activity_execution_id` como chave padrão de idempotência de uma operação externa. Use `activity_attempt_id` apenas quando o destino externo realmente precisar distinguir cada tentativa. + +```php +use Workflow\V2\Activity; + +final class ChargeCard extends Activity +{ + public function handle(array $payload): string + { + return app(PaymentGateway::class)->charge( + $payload, + idempotencyKey: $this->activityId(), + attemptCorrelation: $this->attemptId(), + ); + } +} +``` + +## O que a aplicação precisa tornar idempotente {#what-developers-must-make-idempotent} + +Você **não** precisa tornar o próprio replay idempotente. O framework reconstrói o estado a partir do histórico confirmado. + +Você **precisa** tornar seguros para repetição os efeitos externos, como: + +- chamadas de pagamento ou cobrança +- e-mails, mensagens de texto e webhooks +- gravações em outro banco de dados ou serviço +- criação ou upload de arquivos +- qualquer comando que crie ou modifique estado fora do histórico do workflow + +Abordagens comuns: + +- Passe uma chave de idempotência à API remota. +- Grave em um recurso de destino determinístico, como uma chave de objeto conhecida. +- Use um upsert ou uma transação com um identificador durável como chave. +- Torne a ação naturalmente repetível, para que a segunda chamada não altere nada. + +## Orientações para operação {#operator-guidance} + +Ao diagnosticar uma execução no Waterline, na CLI ou nos logs do Server: + +- Considere normal observar uma atividade novamente após a expiração de um lease, até que o resultado durável da tentativa indique um problema. +- Considere relatos tardios de conclusão ou falha como uma disputa que o mecanismo resolve. Eles não provam que o efeito externo deixou de acontecer. +- Trate o replay de uma tarefa de workflow como recuperação, e não como uma nova tentativa do workflow inteiro. +- Investigue workers compatíveis ausentes, leases presos ou reparos repetidos como sinais operacionais. Eles não justificam repetir efeitos externos no corpo do workflow. + +A distinção essencial para a operação é entre **incerteza do transporte** e **resultado durável**. Durable Workflow expõe ambos para que você possa distingui-los. + +## Guias relacionados {#related-guides} + +- [Visão geral](./overview.md) apresenta a divisão entre workflows e atividades. +- [Restrições dos workflows](./workflow-constraints.md) descreve as regras de código determinístico. +- [Restrições das atividades](./activity-constraints.md) explica a segurança de efeitos externos e as técnicas de idempotência. +- [Falhas e recuperação](../failures-and-recovery.md) aborda novas tentativas, aplicação de timeouts e reparo. +- [Modelo de execução de atividades](../features/activity-execution-model.md) explica atividades em fila, atividades locais, sessões de workers e execução sticky. +- [Atividades locais](../features/local-activities.md) explica tentativas no mesmo processo, heartbeats de tarefas de workflow, novas tentativas e replay sem cache. +- [Execução sticky](../features/sticky-execution.md) explica os caches de replay e por que o replay sem cache continua sendo a alternativa que preserva a correção. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/overview.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/overview.md new file mode 100644 index 0000000000..975f25865e --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/overview.md @@ -0,0 +1,21 @@ +--- +sidebar_position: 1 +title: Visão geral das restrições +description: Entenda as regras de determinismo dos workflows e de idempotência das atividades. +--- + +# Visão geral {#overview} + +Workflows e atividades ficam em lados diferentes da fronteira durável e seguem restrições diferentes. O código do workflow passa por **replay**. O código da atividade pode ser **reexecutado em uma nova tentativa**. Essas operações têm regras distintas. + +Comece por [Workflows idempotentes e determinísticos](/docs/constraints/idempotent-vs-deterministic/) para comparar as duas propriedades e ver exemplos de por que uma não implica a outra. + +- **O código de orquestração do workflow deve ser determinístico.** O mecanismo faz replay do histórico para reconstruir o estado sempre que o workflow retoma a execução, seja em outro worker, após uma reinicialização, após uma implantação ou durante uma execução longa. O replay executa novamente o corpo do workflow, sem executar novamente as atividades. Por isso, o workflow deve tomar as mesmas decisões, na mesma ordem, sempre que receber o mesmo histórico. Consultar o relógio do sistema ou um cache em tempo real, gerar números aleatórios, fazer chamadas de rede e acessar outras fontes de mudança são operações proibidas no corpo do workflow. Use [`Workflow::now()`](../defining-workflows/workflow-api.md), [`sideEffect(...)`](../features/side-effects.md), atividades e outros recursos apropriados para atravessar a fronteira durável. + +- **O código das atividades deve ser idempotente.** As tentativas de atividade têm execução **pelo menos uma vez**. Novas tentativas, expiração de leases e reentrega podem fazer com que o mesmo trabalho lógico seja observado mais de uma vez. Esse comportamento faz parte do contrato. O framework registra no máximo um resultado terminal por tentativa na camada de estado durável, mas o corpo de uma atividade pode começar a executar mais de uma vez antes de o mecanismo receber o relato vencedor. Considere a repetição normal e use uma chave de idempotência, um recurso de destino determinístico ou uma operação naturalmente idempotente quando o efeito externo não puder ser duplicado. + +- **Event sourcing persiste o histórico.** O mecanismo grava cada etapa durável, como uma atividade concluída, um temporizador disparado, um sinal recebido ou um efeito registrado, como um evento de histórico tipado. O replay lê esse histórico e devolve os resultados registrados ao corpo do workflow, sem despachar novamente atividades, temporizadores ou sinais. Para um mesmo identificador, os eventos de estado durável são registrados exatamente uma vez na camada de histórico, mesmo quando o transporte entrega o trabalho mais de uma vez. + +Juntos, determinismo e idempotência permitem retomar workflows que atravessam implantações, reinicializações de workers e novas tentativas distribuídas, sem perder o ponto de execução e sem duplicar efeitos externos que a aplicação já tornou seguros para repetição. + +Veja [Garantias de execução e idempotência](./execution-guarantees.md) para o contrato público da v2 sobre replay, reentrega, expiração de leases e histórico durável registrado exatamente uma vez. Depois, consulte [Restrições dos workflows](./workflow-constraints.md) para as regras de código que mantêm o replay determinístico e [Restrições das atividades](./activity-constraints.md) para tornar segura a execução de atividades pelo menos uma vez. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/workflow-constraints.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/workflow-constraints.md new file mode 100644 index 0000000000..71b03a5e2f --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/constraints/workflow-constraints.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 2 +title: Restrições dos workflows +description: Mantenha o replay determinístico e identifique chamadas inseguras na inicialização da aplicação. +--- + +# Restrições dos workflows {#workflow-constraints} + +Uma classe de workflow não deve depender de estado externo ou de serviços que possam mudar ao longo do tempo. Isso inclui operações que dependam da data e hora atuais, do usuário conectado, de recursos de rede externos ou de qualquer outra fonte de estado variável. + +Evite estas operações dentro de uma classe de workflow: + +- Não use `Carbon::now()` para obter a data e hora atuais, pois o resultado muda a cada chamada. Use `Workflow\V2\Workflow::now()` ou a função `Workflow\V2\now()`, que retornam o tempo do workflow de forma segura para replay. +- Não use `Auth::user()` para obter o usuário atual, pois o resultado depende de quem está conectado. Passe o usuário como entrada ao iniciar o workflow. +- Não faça chamadas de rede para recursos externos, que podem estar lentos ou indisponíveis em momentos diferentes. Passe os dados necessários como entrada ao iniciar o workflow ou use uma atividade para buscá-los. +- Não use geradores de números aleatórios, exceto dentro de um efeito registrado com `sideEffect`, nem outras fontes de aleatoriedade. Passe os valores aleatórios necessários como entrada ao iniciar o workflow. + +## Verificações na inicialização {#boot-time-guardrails} + +Quando classes de workflow são registradas em `workflows.v2.types.workflows`, o pacote analisa o código na inicialização da aplicação para identificar chamadas obviamente inseguras para replay, como `Carbon::now()`, `Auth::user()`, `DB::`, `Http::` e `random_int()`. O tratamento dos resultados é definido por `workflows.v2.guardrails.boot`: + +| Modo | Comportamento | +| --- | --- | +| `warn` (padrão) | Registra um aviso para cada ocorrência detectada, sem impedir a inicialização. | +| `silent` | Desativa a análise na inicialização. | +| `throw` | Lança uma `LogicException` na primeira ocorrência. Útil em pipelines de CI. | + +```php +// config/workflows.php +'v2' => [ + 'guardrails' => [ + 'boot' => env('DW_V2_GUARDRAILS_BOOT', 'warn'), + ], +], +``` + +Configure `DW_V2_GUARDRAILS_BOOT=throw` na CI para reprovar builds que introduzam chamadas inseguras para replay. Mantenha `warn` em produção para que uma ocorrência ainda não identificada não bloqueie uma implantação. + +Na versão inicial, a análise na inicialização é a única verificação de determinismo que pode bloquear a execução. O runtime não repete esse diagnóstico ao adquirir uma tarefa de workflow. Essa escolha é intencional na 2.0: a análise identifica problemas nos workflows PHP registrados localmente antes da implantação, e o Waterline mostra mudanças na impressão digital da definição em execuções longas, sem tornar a aquisição de tarefas entre builds uma nova fonte de falhas de implantação. + +Execuções anteriores ao registro de impressões digitais também seguem uma política conservadora. Se uma execução alcançar um novo ramo de `getVersion()` e seu histórico `WorkflowStarted` for anterior ao registro da impressão digital, o runtime mantém essa execução em `WorkflowStub::DEFAULT_VERSION`, sem presumir que a definição atual é segura. Veja [Versionamento](../features/versioning.md). diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/defining-workflows/workflows.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/defining-workflows/workflows.md new file mode 100644 index 0000000000..527d82a446 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/defining-workflows/workflows.md @@ -0,0 +1,38 @@ +--- +sidebar_position: 1 +title: Workflows +description: Defina classes de workflow da v2 e mantenha o código de orquestração determinístico. +tags: + - authoring + - workflows + - determinism +keywords: + - classe de workflow + - código de workflow v2 + - workflow determinístico +--- + +# Workflows + +Workflows e atividades são definidos como classes que estendem as classes base `Workflow` e `Activity` fornecidas pelo framework. Um workflow define uma sequência de atividades que podem executar em paralelo, em série ou em uma combinação das duas formas. + +Use o comando Artisan `make:workflow` para criar um workflow: + +```php +php artisan make:workflow MyWorkflow +``` + +Estenda a classe `Workflow` e implemente o método `handle()`: + +```php +use function Workflow\V2\activity; +use Workflow\V2\Workflow; + +class MyWorkflow extends Workflow +{ + public function handle() + { + return activity(MyActivity::class); + } +} +``` diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/failures-and-recovery.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/failures-and-recovery.md new file mode 100644 index 0000000000..fd6245f21b --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/failures-and-recovery.md @@ -0,0 +1,305 @@ +--- +sidebar_position: 11 +title: Falhas e recuperação +description: Diagnostique falhas de atividades, exceções permanentes, timeouts de execução, limpeza de cancelamento e ações de recuperação. +tags: + - failures + - recovery + - operations +keywords: + - falhas de workflow + - exceção permanente + - recuperação de workflow +--- + +# Falhas e recuperação {#failures-and-recovery} + +Antes de investigar uma falha, lembre-se do contrato central de execução: + +- tarefas de workflow se recuperam por replay do histórico confirmado +- atividades executam pelo menos uma vez e podem ser observadas mais de uma vez +- expiração de leases e reentrega são caminhos normais de recuperação, sem + provar que o worker anterior deixou de realizar o efeito externo + +Leia [Garantias de execução e idempotência](./constraints/execution-guarantees.md) +para entender as regras exatas de novas tentativas, reentrega e resultados +duráveis. + +## Tratamento de exceções {#handling-exceptions} + +Quando uma atividade lança uma exceção, o workflow aguarda o esgotamento das +tentativas definidas em `$tries`. O sistema continua tentando executar a +atividade de acordo com sua política. Para entregar a exceção ao workflow +imediatamente após a primeira falha, defina `$tries` como 1. + +```php +use Exception; +use Workflow\V2\Activity; + +class MyActivity extends Activity +{ + public int $tries = 1; + + public function handle(): void + { + throw new Exception(); + } +} +``` + +```php +use Exception; +use function Workflow\V2\activity; +use Workflow\V2\Workflow; + +class MyWorkflow extends Workflow +{ + public function handle(): void + { + try { + $result = activity(MyActivity::class); + } catch (Exception) { + // handle the exception here + } + } +} +``` + +## Exceções que não permitem novas tentativas {#non-retryable-exceptions} + +Algumas exceções representam falhas permanentes que não devem provocar uma +nova tentativa. Quando uma atividade lança uma dessas exceções, o mecanismo +marca a atividade como falha imediatamente e interrompe as tentativas. + +```php +use Workflow\V2\Activity; +use Workflow\Exceptions\NonRetryableException; + +class MyNonRetryableActivity extends Activity +{ + public function handle(): void + { + throw new NonRetryableException('This is a non-retryable error'); + } +} +``` + +## Processo de recuperação {#recovery-process} + +Para corrigir uma atividade que está falhando: + +1. Consulte seus logs e identifique os erros ou exceções. +2. Encontre a causa e corrija o código. +3. Implante a correção no servidor que executa a fila. +4. Reinicie ou substitua gradualmente os workers relevantes para que carreguem + o novo código e possam adquirir o trabalho com segurança. +5. Aguarde a próxima tentativa da atividade ou a entrega da tarefa durável a um + worker saudável pelo mecanismo de reparo ou reentrega. +6. Verifique o resultado durável no Waterline, na exportação do histórico ou na + API do Server. Uma linha de log de um worker não é a fonte de verdade. +7. Se a atividade continuar falhando, repita o processo até resolver a causa. + +Durante as novas tentativas de uma atividade, o workflow pode permanecer em +estado de execução. Ao corrigir a atividade antes que essas tentativas se +esgotem, o workflow pode concluir normalmente. Se todas as tentativas de +`$tries` se esgotarem e a exceção não for tratada, o workflow termina em +estado de falha. + +## Aplicação de timeouts do workflow {#workflow-timeout-enforcement} + +Quando `StartOptions::withExecutionTimeout()` ou +`StartOptions::withRunTimeout()` é configurado, o mecanismo registra um prazo +na execução do workflow. O prazo de execução lógica abrange o workflow inteiro, +incluindo execuções criadas por continue-as-new. O prazo de uma execução +individual recomeça em cada nova execução. + +Se o prazo já tiver passado quando o mecanismo iniciar uma tarefa de workflow, +a execução é encerrada imediatamente: + +- Atividades abertas, temporizadores e tarefas pendentes são cancelados, com + eventos de histórico tipados como `ActivityCancelled` e `TimerCancelled`. +- Um registro `WorkflowFailure` é criado com `failure_category = timeout` e + `propagation_kind = timeout`. +- Um evento `WorkflowTimedOut` é registrado com `timeout_kind` igual a + `execution_timeout` ou `run_timeout`. +- O estado da execução passa a `failed`, com `closed_reason = timed_out`. +- Workflows pais que aguardam o filho são notificados. + +O monitor de tarefas em segundo plano também procura execuções não terminais +com prazos vencidos e sem tarefa de workflow aberta, como uma execução que +aguarda uma atividade ou temporizador. Ao encontrar uma, cria uma tarefa para +que o executor detecte e aplique o timeout na próxima passagem. + +Waterline mostra `failure_category` na coluna **Category** da tabela de +exceções e nos detalhes de falhas da linha do tempo. Exportações de histórico +incluem `failure_category` em `failures[*]`. A v2 final grava essa +classificação quando registra a falha. Registros importados da v1 que não podem +ser classificados continuam visíveis como diagnósticos sem classificação. + +## Novas tentativas de atividades {#activity-retries} + +O padrão de `Workflow\V2\Activity` é `$tries = 1`. Assim, uma falha é +entregue ao workflow imediatamente, a menos que a atividade configure mais +tentativas. + +```php +use RuntimeException; +use Workflow\V2\Activity; + +class ChargeCard extends Activity +{ + public int $tries = 3; + + public function backoff(): array + { + return [5, 30]; + } + + public function handle(): string + { + throw new RuntimeException('temporary gateway failure'); + } +} +``` + +Quando uma atividade com falha transitória lança uma exceção antes de esgotar +`$tries`, o mecanismo encerra o registro atual de `activity_attempts` no +estado do runtime, devolve o registro de `activity_executions` a `pending`, +grava um evento tipado `ActivityRetryScheduled` e cria uma nova tarefa durável. +O valor de `available_at` segue a política de `backoff()`. O workflow continua +aguardando a mesma execução de atividade. Ele só recebe a exceção quando falha +a última tentativa permitida. + +A tarefa registra `retry_of_task_id`, `retry_after_attempt_id`, +`retry_after_attempt` e `retry_backoff_seconds` em seu payload, para que +Waterline explique o agendamento. Os detalhes da execução selecionada +reconstroem a tentativa com falha em `activities[*].attempts` a partir do +histórico tipado de atividades, mostram `ActivityRetryScheduled` na linha do +tempo e informam as contagens de novas tentativas em +`operator_metrics.activities.retrying`, +`operator_metrics.activities.failed_attempts` e +`operator_metrics.backlog.retrying_activities`. + +`Workflow\Exceptions\NonRetryableExceptionContract` interrompe a política de +novas tentativas. Uma exceção que implementa esse contrato faz a atividade +falhar imediatamente e retoma o workflow com a exceção. + +### Identidade e idempotência da atividade {#activity-execution-identity-and-idempotency} + +Uma nova tentativa não é o único motivo para observar a mesma atividade lógica +mais de uma vez. Expiração de lease, perda de worker, atraso no relato de +conclusão e reentrega podem produzir outra tentativa ou um relato de conclusão +obsoleto para a mesma execução durável. + +- `activity_execution_id` identifica a atividade lógica ao longo de novas + tentativas e reentregas. Use-o como chave padrão de idempotência de efeitos + remotos. +- `activity_attempt_id` identifica uma tentativa específica. Use-o apenas + quando o sistema de destino precisa distinguir as tentativas. +- Um relato tardio de conclusão ou falha de uma tentativa substituída é um + comportamento normal de tentativa obsoleta. Não prova que o mecanismo + confirmou a mesma tentativa duas vezes. + +Ao investigar uma conclusão tardia após a expiração de um lease: + +- consulte Waterline, a exportação de histórico ou a API do Server para saber + qual tentativa venceu a disputa pelo resultado durável +- não presuma que a rejeição do relato tardio significa que o efeito remoto não + aconteceu +- verifique o sistema externo pela chave de idempotência antes de forçar uma + nova tentativa ou um reparo manual + +O padrão mais seguro é tornar o efeito remoto idempotente por +`activity_execution_id` e usar o resultado durável para saber se o mecanismo +aceitou o relato daquela tentativa específica. + +### Marcadores de falha permanente {#non-retryable-failure-markers} + +Quando uma atividade ou workflow lança uma exceção que implementa +`Workflow\Exceptions\NonRetryableExceptionContract`, o mecanismo grava +`non_retryable = true` no registro `WorkflowFailure` e no payload do evento +tipado, como `ActivityFailed`, `WorkflowFailed` ou `UpdateCompleted`. +Esse marcador durável informa a operadores, workers externos e ferramentas que +a falha é permanente e que repetir a mesma operação não terá sucesso. + +O marcador aparece em toda a camada de visibilidade: + +- **Registros de falha:** coluna booleana `workflow_failures.non_retryable`. +- **Eventos de histórico:** campo `non_retryable` no payload tipado. +- **Snapshots de falha:** `non_retryable` em `FailureSnapshots::forRun()`. +- **Detalhes da execução:** `non_retryable` na lista de exceções. +- **Linha do tempo:** `non_retryable` nos metadados de detalhes da falha. +- **Exportações de histórico:** `non_retryable` em `failures[*]`. +- **Waterline:** marcador “non-retryable” ao lado da categoria de falha na tabela + de exceções e na linha do tempo. +- **Integração de workers externos:** o payload do comando `complete()` aceita + `non_retryable`. Workers externos podem informar falhas permanentes sem + exigir que o processo do runtime resolva a classe da exceção. + +Para falhas que não implementam o contrato, `non_retryable` é `false` por +padrão. A v2 final grava o marcador no momento da falha. Declare o contrato antes +de registrar a falha quando operadores ou SDKs precisarem distinguir falhas +permanentes das que permitem novas tentativas. + +```php +use Workflow\Exceptions\NonRetryableExceptionContract; + +class PaymentDeclinedException extends \RuntimeException implements NonRetryableExceptionContract +{ + // This failure will be marked as non-retryable in the durable record. +} +``` + +## Novas tentativas do workflow inteiro {#workflow-level-retry} + +Durable Workflow v2 **não** inicia automaticamente uma nova tentativa do +workflow inteiro. Quando uma execução falha por exceção não tratada, limite +estrutural ou timeout, seu estado é terminal. O mecanismo não inicia +automaticamente outra execução da mesma instância. + +Essa é uma escolha de projeto: + +- **Atividades já oferecem novas tentativas.** Políticas com `$tries`, + `backoff()` e exceções permanentes tratam falhas transitórias na etapa + apropriada. +- **O replay é o mecanismo de recuperação.** Se uma tarefa de workflow encontra + uma falha transitória de infraestrutura, como erro de banco ou perda de + worker, o sistema de tarefas duráveis despacha a tarefa novamente e retoma + pelo histórico confirmado, sem criar outra execução. +- **Continue-as-new atende workflows longos.** Workflows que precisam renovar o + estado ou compactar o histórico usam `continueAsNew()` como reinício + explícito. +- **O reparo recupera execuções presas.** O comando `repair()` e o reparo + automático no loop do worker recuperam execuções cujo transporte de tarefas + duráveis foi perdido. + +Se sua aplicação precisa tentar novamente o workflow inteiro, modele esse +comportamento explicitamente: + +```php +use function Workflow\V2\activity; +use Throwable; +use Workflow\V2\Workflow; + +class RetryableWorkflow extends Workflow +{ + public function handle(string $orderId): void + { + try { + activity(ProcessOrderActivity::class, $orderId); + } catch (Throwable $e) { + // Record the failure, then start a new workflow + // for retry-at-workflow-level scenarios. + activity(NotifyFailureActivity::class, $orderId, $e->getMessage()); + } + } +} +``` + +## Guias relacionados {#related-guides} + +- [Garantias de execução e idempotência](./constraints/execution-guarantees.md) + explica o contrato de replay, novas tentativas, expiração de leases e + reentrega que orienta todos os caminhos de recuperação desta página. +- [Monitoramento](./monitoring.md) mostra onde Waterline, exportação de histórico, + logs de workers e telemetria do runtime expõem as informações de falha. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/how-it-works.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/how-it-works.md new file mode 100644 index 0000000000..00f22b4e13 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/how-it-works.md @@ -0,0 +1,109 @@ +--- +sidebar_position: 10 +title: Como funciona +description: Entenda o replay, as atividades e a persistência durável em um aplicativo Laravel. +--- + +# Como funciona {#how-it-works} + +Durable Workflow usa os jobs em fila do Laravel e a persistência baseada em eventos para criar corrotinas duráveis. Funções baseadas em Fibers suspendem os workflows e permitem reconstruir sua execução por replay. + +## Runtime {#runtime} + +Um workflow é uma classe cujo método `handle()` chama funções como `activity()`, `await()`, `timer()`, `sideEffect()`, `child()` e `all([...])` diretamente, em código sequencial. Cada chamada suspende o workflow até que a etapa durável correspondente termine. A execução então retoma de onde parou, com o resultado registrado. + +Cada etapa produz um evento de histórico durável. Sempre que o workflow é reativado, o mecanismo faz replay desse histórico e reconstrói o estado a partir dos eventos antes de executar a próxima etapa pendente. Isso permite sobreviver a reinicializações de workers, implantações e falhas de máquinas sem perder o ponto de execução. + +`WorkflowStub::make()` reserva um identificador público de instância. Iniciar o workflow cria a primeira execução e a primeira tarefa de workflow. Cada execução tem seu próprio identificador. Operações como `signal()`, `cancel()` e `terminate()` atuam na execução atual da instância. + +## Event sourcing {#event-sourcing} + +Event sourcing reconstrói o estado atual a partir de uma sequência de eventos salvos, em vez de salvar o estado diretamente. Isso fornece um histórico completo dos eventos de execução e permite retomar um workflow se o worker falhar. + +## Corrotinas {#coroutines} + +Corrotinas são funções cuja execução pode ser suspensa e retomada. Os pontos de suspensão duráveis são expressos por chamadas diretas a funções baseadas em Fibers, como `activity()`, `await()`, `timer()` e `sideEffect()`. + +O código do workflow fica em `handle()`, um método comum que chama essas funções diretamente. O runtime primeiro verifica se a etapa já terminou de forma durável. Se terminou, devolve o resultado registrado no histórico, sem executar a etapa outra vez. Caso contrário, coloca a próxima atividade, temporizador ou tarefa filha na fila e suspende o workflow até que a etapa termine ou falhe. + +## Atividades {#activities} + +Um workflow coordena várias atividades e seus resultados. A execução do workflow se alterna com as etapas duráveis que ele agenda: ao chegar a uma chamada de atividade, o workflow suspende a execução até a atividade terminar e então continua de onde parou. + +Se um worker de workflow falhar, o replay dos eventos confirmados reconstrói o estado atual. O workflow pode continuar de onde parou, com as mesmas entradas e saídas, preservando o determinismo. Uma falha não tratada no workflow torna a execução terminal. O replay não tenta novamente uma execução que já falhou. + +Na v2, atividades comuns são trabalho durável em fila. As +[atividades locais](./features/local-activities.md) executam tarefas curtas no +processo do worker de workflow, preservando o histórico durável e as regras de +novas tentativas. Atividades comuns podem executar em qualquer worker +compatível. As [sessões de workers](./features/worker-sessions.md) adicionam um +lease explícito quando uma sequência de atividades precisa do mesmo recurso +local de um worker. Para registrar um valor uma única vez, de forma segura para +replay e sem colocar uma atividade na fila, use +[`sideEffect(...)`](./features/side-effects.md). Veja o contrato completo em +[Modelo de execução de atividades](./features/activity-execution-model.md). + +## Garantias de execução {#execution-guarantees} + +O código do workflow e o código da atividade têm regras diferentes para repetição: + +- **O código do workflow passa por replay.** A reentrega de uma tarefa de workflow reconstrói o estado a partir do histórico durável e executa novamente o código determinístico de orquestração. O replay não repete efeitos externos já registrados. +- **Atividades são trabalho em fila executado pelo menos uma vez.** Uma atividade lógica pode passar por novas tentativas, ser reentregue após a expiração de um lease ou ser observada novamente após a perda de um worker. Entregas duplicadas são uma condição normal em sistemas distribuídos. +- **A identidade da atividade é durável.** `activity_execution_id` identifica uma execução lógica de atividade ao longo de novas tentativas e reentregas. `activity_attempt_id` identifica uma tentativa individual. Use `activity_execution_id` como chave padrão de idempotência no sistema remoto. Use `activity_attempt_id` apenas quando esse sistema precisa de correlação por tentativa. + +Veja o contrato completo da v2 em +[Garantias de execução e idempotência](./constraints/execution-guarantees.md), +[Modelo de execução de atividades](./features/activity-execution-model.md) e +[Falhas e recuperação](./failures-and-recovery.md). + +## Filas {#queues} + +Jobs em fila são tarefas de segundo plano que executam mais tarde. O Laravel oferece filas com Amazon SQS, Redis ou um banco de dados relacional. Workflows e atividades são jobs em fila, com comportamentos diferentes. Um workflow é despachado várias vezes durante a operação normal: executa, despacha uma ou mais atividades e encerra seu job até que elas terminem. Uma atividade é uma tarefa em fila executada pelo menos uma vez. O caso comum é uma tentativa bem-sucedida, mas novas tentativas, expiração de leases ou perda de um worker podem causar a reentrega da mesma execução lógica de atividade. + +## Exemplo {#example} + +```php +use Workflow\V2\Workflow; +use function Workflow\V2\{activity, all}; + +class MyWorkflow extends Workflow +{ + public function handle(): array + { + return [ + activity(TestActivity::class), + activity(TestOtherActivity::class), + all([ + fn () => activity(TestParallelActivity::class), + fn () => activity(TestParallelOtherActivity::class), + ]), + ]; + } +} +``` + +## Diagrama de sequência {#sequence-diagram} + +Este diagrama mostra a progressão de um workflow por uma série de atividades, tanto em sequência quanto em paralelo. + +import ThemedImage from '@site/src/components/ThemedImage'; + + + +1. O workflow começa ao ser despachado como um job em fila. +2. A primeira atividade, `TestActivity`, é despachada como um job em fila. O job do workflow então encerra. Quando `TestActivity` termina, salva o resultado no banco de dados e despacha o workflow novamente. +3. O workflow entra no ciclo de replay do event sourcing. Ele consulta os eventos no banco de dados para reconstruir o estado atual. Isso é necessário porque o workflow não mantém um processo em execução contínua. Seu job encerra enquanto as atividades executam e é despachado novamente quando elas terminam. +4. Após o replay dos eventos, o workflow continua para `TestOtherActivity` e a despacha como um job em fila. Quando essa atividade termina, salva o resultado no banco de dados e despacha o workflow novamente. +5. O workflow faz outro replay dos eventos para reconstruir o estado atual. +6. Em seguida, o workflow inicia duas atividades em paralelo, `TestParallelActivity` e `TestParallelOtherActivity`. Ambas são despachadas. Quando terminam, salvam seus resultados no banco de dados e devolvem o controle ao workflow. +7. Por fim, o workflow faz replay dos eventos uma última vez para reconstruir o estado atual e concluir a execução. + +## Determinismo {#determinism} + +Como o histórico passa por replay sempre que o workflow é reativado, seu código deve produzir os mesmos comandos para o mesmo histórico. Veja [Restrições](./constraints/overview.md) para as regras de código e os recursos que Durable Workflow fornece, como `Workflow\now()`, `sideEffect()` e `getVersion()`, quando o código seria não determinístico. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md index f31c4c1f4f..06aa76525c 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/introduction.md @@ -9,7 +9,7 @@ keywords: - durable workflow - execução durável - orquestração durável - - workflows em vários linguagens + - workflows em várias linguagens --- import PythonPackageReleaseLink from '@site/src/components/PythonPackageReleaseLink'; @@ -68,7 +68,7 @@ de alterar o tráfego. - **[SDK Python](/docs/polyglot/python/):** escreva workflows determinísticos e atividades e use o cliente assíncrono do plano de controle. A versão estável do - Python consta no mesmo manifesto de versões + SDK Python consta no mesmo manifesto de versões estáveis usado pelo guia de início rápido do Server. - **[SDK Rust](/docs/polyglot/rust/):** escreva workflows determinísticos e atividades e execute serviços de workers nativos. diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/polyglot/deployment-modes.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/polyglot/deployment-modes.md new file mode 100644 index 0000000000..0da490ef59 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/polyglot/deployment-modes.md @@ -0,0 +1,160 @@ +--- +sidebar_position: 2 +title: Modos de implantação +description: Escolha o modo serviço com Durable Workflow Cloud ou Server sob sua administração, ou integre o runtime ao Laravel. +tags: + - deployment + - server + - laravel + - polyglot +keywords: + - modos de implantação Durable Workflow + - modo integrado + - modo serviço + - Durable Workflow Cloud + - Server independente + - Cloud ou infraestrutura própria +--- + +import ProductPromotion from '@site/src/components/ProductPromotion'; + +# Modos de implantação {#deployment-modes} + + +Escolha o modo serviço gerenciado para seus workers PHP, Python ou Rust sem +operar o runtime de orquestração. + + +Durable Workflow v2 tem dois modos de implantação: + +- **Modo serviço:** aplicações e workers se conectam por SDKs a um runtime + remoto. Escolha [Durable Workflow Cloud](/docs/polyglot/cloud-control-plane/) + ou [Server sob sua administração](/docs/polyglot/server/). +- **Modo integrado:** uma aplicação Laravel instala + `durable-workflow/workflow` e opera o runtime diretamente. + +Cloud e Server sob sua administração são opções de runtime dentro do modo +serviço. Não são componentes para executar juntos. No Cloud, Durable Workflow +opera a orquestração e a persistência, enquanto os clientes executam os clientes +dos SDKs e os workers. O Cloud inclui Managed Waterline. **Clientes do Cloud não +instalam, implantam nem conectam um serviço próprio de Server ou Waterline.** +Um Server sob sua administração não inclui Waterline. Operadores podem implantá-lo +separadamente, conectado a um namespace pertencente ao Server. + +Use esta página para decidir quem deve operar uma frota de workflows, planejar +a transição entre modos ou documentar quais partes do contrato permanecem +iguais nos dois modos. + +Equipes Laravel podem seguir o +[Guia de adoção do Laravel e transição de runtime](/docs/laravel-adoption/) +para os caminhos da v1 para v2 e do modo integrado para serviço, incluindo a +integração publicada do SDK PHP e seu fake de testes para Laravel. + +## Escolha um runtime em modo serviço {#choose-a-service-mode-runtime} + +| Runtime | Quem opera o estado durável | O que sua equipe executa | Comece aqui | +| --- | --- | --- | --- | +| Durable Workflow Cloud | Durable Workflow opera o runtime gerenciado do namespace, a persistência, as atualizações, o endpoint de serviço e o Managed Waterline. | Clientes da aplicação e workers PHP, Python ou Rust com credenciais provisionadas. Não execute Server nem um serviço Waterline separado. | [Runtime gerenciado do Cloud](/docs/polyglot/cloud-control-plane/) | +| Server sob sua administração | Sua equipe implanta, protege, escala, faz backup e atualiza o Server e sua persistência. | Server, clientes da aplicação e workers PHP, Python ou Rust. Se quiser, implante Waterline como serviço separado conectado a um namespace pertencente ao Server. | [Server sob sua administração](/docs/polyglot/server/) | + +As duas opções usam o mesmo modelo de cliente e worker. A diferença está na +operação do runtime e nas credenciais, não no código dos workflows. + +## Mesmo modelo durável, outra fronteira {#same-durable-model-different-boundary} + +O modo integrado e o modo serviço usam o mesmo núcleo v2. O que muda é a +fronteira de hospedagem, autenticação e transporte ao redor dele. O modo serviço +expõe o mesmo núcleo por APIs HTTP+JSON para o plano de controle e os workers. +Não há gRPC obrigatório nem um segundo mecanismo. + +| Recurso | Modo integrado | Modo serviço | O que permanece igual | +| --- | --- | --- | --- | +| Modelo de workflow durável | A aplicação Laravel hospeda o pacote e grava o estado no runtime da aplicação. | Cloud ou Server sob sua administração mantém o estado por trás da API de serviço. | Identificadores de workflow e execução, histórico tipado, resultados de comandos, novas tentativas, regras de reparo e exportação de histórico seguem o mesmo contrato v2. | +| Plano de controle | Inícios e comandos vêm do código da aplicação, de `WorkflowStub` ou das ferramentas locais de operação. | Inícios e comandos passam pela API do Server, CLI ou SDKs em HTTP+JSON, com autenticação e cabeçalhos de protocolo explícitos. Clientes PHP independentes de framework usam `DurableWorkflow\Client` do pacote `durable-workflow/sdk`. | Política de início duplicado, seleção de execução, identificadores de comando e resultados nomeados permanecem iguais. Envie os comandos seguintes ao runtime que aceitou o início. | +| Transporte dos workers | Workers de fila do Laravel executam tarefas de workflow e atividade na implantação da aplicação. | Workers se registram, fazem long polling, enviam heartbeats e concluem trabalho pelo protocolo HTTP+JSON. Workers PHP remotos usam `DurableWorkflow\Worker` do pacote `durable-workflow/sdk`. | Leases de tarefas, marcadores de compatibilidade, regras de replay e execução de atividades pelo menos uma vez permanecem iguais. | +| Despacho padrão de tarefas | Normalmente, as tarefas são enviadas à fila Laravel no processo da aplicação. | O runtime de serviço usa despacho por polling para que workers externos encontrem trabalho por HTTP. Operadores de Server sob sua administração podem alterar esse padrão explicitamente. | O ciclo de tarefa pronta, adquirida e reparada e o modelo de tarefa durável permanecem iguais. | +| Chaves de tipos de workflow e atividade | Aliases PHP podem apontar para classes locais da aplicação. | Workers anunciam as chaves de tipos suportadas no registro. | As chaves públicas devem ser estáveis e independentes de linguagem. Nomes de classe PHP totalmente qualificados ou tipos PHP espelhados não devem ser o contrato público. | +| Ferramentas de operação | O pacote Waterline integrado ou as ferramentas locais leem o estado durável da aplicação Laravel no próprio processo. | Cloud oferece Managed Waterline para o namespace. Operadores de Server podem implantar Waterline separadamente. APIs de serviço, CLI e SDKs também leem o estado mantido pelo runtime. | Atributos de busca, memos, estado da execução, diagnóstico de filas e exportação de histórico são fatos duráveis no runtime que possui a execução. Waterline não combina runtimes nem namespaces. | +| Autenticação e isolamento | A aplicação Laravel define a autenticação de suas próprias rotas e sessões. | A seleção de namespace e os tokens ou assinaturas de autenticação do Server são fronteiras obrigatórias da API. | Nomes de namespace, filas de tarefas, marcadores de compatibilidade e contrato fixo de payload Avro devem permanecer estáveis na transição. | +| Descoberta do runtime | A aplicação resolve serviços no processo ou pela configuração local. | Workers e clientes usam uma URL base remota explícita. | Não dependa de `APP_URL` ou `APP_KEY` compartilhados, de pressupostos sobre localhost nem de descoberta no mesmo container. | +| Migração | Execuções integradas existentes continuam onde começaram. | Novas execuções em modo serviço começam no Cloud ou Server escolhido e permanecem lá. | Não há migração automática de execuções em andamento entre modos. A exportação serve para auditoria e diagnóstico, não para importar estado vivo. | + +## Quando escolher o modo integrado {#choose-embedded-mode-when} + +- Sua aplicação Laravel reúne código de workflows, execução dos workers e + acesso de operadores em uma implantação. +- O modelo existente de fila e autenticação da aplicação é a fronteira adequada + para operar workflows. +- Você quer um runtime compacto e autossuficiente, sem precisar de um protocolo + de workers independente de linguagem. +- Seus operadores podem usar Waterline ou as ferramentas da aplicação como + interface principal dos workflows. + +Comece por [Instalação integrada](/docs/installation/) e pela +[Documentação integrada](/docs/category/embedded/), incluindo o grupo de +configuração. + +## Quando escolher o modo serviço {#choose-service-mode-when} + +- Várias aplicações ou equipes devem compartilhar um runtime de workflows. +- Workers, clientes do plano de controle ou operadores usam outras tecnologias + além de Laravel e PHP. +- Você precisa de uma fronteira remota explícita de autenticação e namespace + entre clientes e mecanismo de workflow. +- Você quer escalar a entrada da API, o matching e despacho e os workers de forma + independente, dentro da [Topologia de papéis do Server](/docs/polyglot/server-role-topology) + suportada. +- Com Server sob sua administração, você quer implantar Waterline como + observador de um namespace pertencente ao Server. O Cloud já inclui Managed + Waterline. + +Para um runtime gerenciado, comece por +[Durable Workflow Cloud](/docs/polyglot/cloud-control-plane/). +Para infraestrutura própria, comece por [Server](/docs/polyglot/server/) e +[Implantações sob sua administração](/docs/deployment/). Depois, escolha o +[SDK PHP](/docs/polyglot/php/), [SDK Python](/docs/polyglot/python/) ou +[SDK Rust](/docs/polyglot/rust/). Usuários do Cloud operam pelo Managed Waterline. +Operadores de Server podem consultar a +[Referência da API do Server](/docs/polyglot/server-api-reference/) e +[Monitoramento](/docs/monitoring#waterline-service) ao implantar um serviço +Waterline separado. + +## Ferramentas de migração para modo serviço sob sua administração {#migration-tooling-to-self-hosted-service-mode} + +O caminho suportado do modo integrado para serviço é a adoção em etapas, sem +transferência de estado de execuções em andamento: + +- Siga [Migração do modo integrado para Server](/docs/polyglot/embedded-to-server) + para a transição passo a passo. +- Use `GET /api/cluster/info` para confirmar build, topologia e capacidades do + Server de destino antes de mudar o tráfego. +- Use `POST /api/worker/register` e o protocolo de workers para verificar que + workers externos atendem às chaves de tipos estáveis escolhidas. +- Use `GET /api/system/operator-metrics`, `dw worker:list` ou as telas de operação + do Waterline para verificar o registro de workers e a cobertura da frota + compatível antes de mudar o tráfego de produção. +- Consulte [Capacidades de clientes e workers](/docs/polyglot/cli-python-parity/) + ao substituir chamadas locais do plano de controle por automação via Server. +- Use Managed Waterline do Cloud, Waterline conectado a um runtime sob sua + administração ou a exportação nativa de histórico do Server para auditoria e + diagnóstico. Pacotes exportados não importam execuções vivas gerenciadas pelo + Server. + +Três regras de migração são obrigatórias: + +1. Execuções existentes permanecem no runtime onde começaram. +2. Novas execuções gerenciadas pelo Server usam chaves de tipos, namespaces, + filas de tarefas e o contrato fixo de payload Avro estáveis desde a primeira + etapa da transição. +3. Sinais, consultas, atualizações, reparos, cancelamentos, terminações e + arquivamentos devem ir ao runtime que possui a execução de destino. + +## Referências relacionadas {#related-references} + +- [Instalação](/docs/installation) +- [Server](/docs/polyglot/server) +- [SDK PHP](/docs/polyglot/php) +- [Runtime gerenciado do Cloud](/docs/polyglot/cloud-control-plane) +- [Migração do modo integrado para Server](/docs/polyglot/embedded-to-server) +- [Topologia de papéis do Server](/docs/polyglot/server-role-topology) +- [Referência de configuração do Server](/docs/polyglot/server-config-reference) diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quickstart.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quickstart.md new file mode 100644 index 0000000000..afbfdb51f6 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quickstart.md @@ -0,0 +1,493 @@ +--- +sidebar_position: 2 +title: Primeiros passos com Durable Workflow 2.0 +description: Escolha um runtime em modo serviço e conclua seu primeiro workflow com PHP, Python ou Rust. +tags: + - quickstart + - getting-started + - PHP + - Python + - Rust +keywords: + - primeiros passos Durable Workflow + - tutorial Durable Workflow 2.0 + - SDK PHP independente + - SDK Python + - SDK Rust + - Server com Docker +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import PythonPackageReleaseLink from '@site/src/components/PythonPackageReleaseLink'; + +# Primeiros passos com Durable Workflow 2.0 {#durable-workflow-20-quickstart} + +## Antes de começar {#before-you-begin} + +**Objetivo:** executar um workflow em modo serviço e ler seu resultado durável +concluído com PHP, Python ou Rust. + +**Tempo estimado:** cerca de 15 minutos depois que seu runtime estiver disponível. + +**Resultado final:** o SDK escolhido inicia um worker e um workflow, depois +exibe um identificador de workflow, `status=completed` e `Hello, !`. + +**Pré-requisitos:** + +- `curl` e um terminal +- Docker para o caminho local sob sua administração, ou um namespace provisionado + no Durable Workflow Cloud +- ferramentas de uma linguagem: PHP 8.1+ com Composer, Python 3.10+ ou Rust 1.86+ + +O modo serviço não exige Laravel. O caminho para Laravel integrado aparece +separadamente no fim deste guia. + +## 1. Escolha seu runtime em modo serviço {#1-choose-your-service-mode-runtime} + +| Runtime | Quando escolher | Próxima ação | +| --- | --- | --- | +| Durable Workflow Cloud | Você quer que Durable Workflow opere o runtime, a persistência e o Managed Waterline. | Siga o guia executável [Primeiro workflow no Cloud](/docs/polyglot/cloud-control-plane/#cloud-first-workflow), que conecta os programas completos de PHP, Python e Rust às credenciais provisionadas e a um resultado `completed`. **Não execute Server nem um serviço Waterline separado.** | +| Server sob sua administração | Você quer operar o runtime ou executar este exercício local com os artefatos publicados. | Continue abaixo com Docker e `curl`. Implante Waterline separadamente apenas se quiser sua interface de operação. | + +O exemplo abaixo usa um Server local sob sua administração. Assim, você pode +executá-lo sem criar uma conta nem obter o código-fonte. O Cloud usa o mesmo +modelo de SDK e worker. Substitua a conexão de desenvolvimento local pelos +valores provisionados descritos em +[Runtime gerenciado do Cloud](/docs/polyglot/cloud-control-plane/). + +## 2. Inicie o Server local {#2-start-the-local-server} + +Pule esta etapa se escolheu Cloud. Para o caminho sob sua administração, expanda +e execute a configuração com a versão fixada. Ela inicia um Server que não +depende de um checkout do código-fonte, com SQLite e um token de desenvolvimento. + +
+Iniciar a imagem fixada do Server + + +```bash +export DW_SERVER_IMAGE=%%artifact.serverDockerHubImage%% +export DW_AUTH_TOKEN=dev-token + +docker volume create durable-workflow-quickstart + +docker run --rm \ + -v durable-workflow-quickstart:/app/database \ + -e DW_AUTH_DRIVER=token \ + -e DW_AUTH_TOKEN="$DW_AUTH_TOKEN" \ + "$DW_SERVER_IMAGE" server-bootstrap + +docker rm -f durable-workflow-server >/dev/null 2>&1 || true +docker run -d --name durable-workflow-server \ + -p 8080:8080 \ + -v durable-workflow-quickstart:/app/database \ + -e DW_AUTH_DRIVER=token \ + -e DW_AUTH_TOKEN="$DW_AUTH_TOKEN" \ + "$DW_SERVER_IMAGE" + +until curl -sf http://localhost:8080/api/ready >/dev/null; do sleep 1; done +curl -H "Authorization: Bearer $DW_AUTH_TOKEN" \ + http://localhost:8080/api/cluster/info +``` + +
+ +**Resultado esperado:** a verificação de prontidão é bem-sucedida e a resposta +de informações do cluster identifica o Server local independente. Mantenha-o +em execução enquanto conclui o caminho de uma linguagem. + +## 3. Escolha uma linguagem {#choose-one-language} + +Os três SDKs oficiais têm o mesmo destaque. Apenas a aba selecionada é exibida, +para que você possa seguir um caminho sem percorrer os programas das outras +duas linguagens. + + + + +Requisitos: PHP 8.1 ou superior e Composer. Este é o pacote +`durable-workflow/sdk`, independente de framework, e não o mecanismo integrado +ao Laravel. + +1. **Instale o SDK.** + + +```bash +mkdir durable-workflow-php-quickstart +cd durable-workflow-php-quickstart +composer require %%artifact.phpSdkComposerPackage%% +``` + +2. **Adicione o worker e o cliente.** Expanda o código completo e copie os dois + arquivos para o novo projeto. + +
+Código PHP completo para execução + +O worker registra um tipo de workflow e um tipo de atividade em sua própria +fila de tarefas. + + +```bash +cat > worker.php <<'PHP' +registerActivity( + 'quickstart.greet', + static fn (ActivityContext $context, string $name): string => "Hello, {$name}!", +); + +$worker->registerWorkflow( + 'quickstart.greeter', + static function (WorkflowContext $context, string $name): array { + $greeting = $context->activity('quickstart.greet', [$name]); + + return ['greeting' => $greeting, 'language' => 'php']; + }, +); + +$worker->run(); +PHP +``` + +#### Cliente e leitura do resultado {#client-and-result-reader} + +Este cliente inicia um workflow com nome único, aguarda a execução selecionada +e consulta o estado terminal durável mantido pelo Server. + + +```bash +cat > start.php <<'PHP' +startWorkflow( + workflowType: 'quickstart.greeter', + workflowId: $workflowId, + taskQueue: 'quickstart-php', + input: ['PHP'], +); + +$result = $handle->result(timeoutSeconds: 30); +$execution = $handle->describeSelectedRun(); + +echo "workflow_id={$execution->workflowId}\n"; +echo "status={$execution->status}\n"; +echo 'result='.json_encode($result, JSON_THROW_ON_ERROR)."\n"; +PHP +``` + +
+ +3. **Execute o worker e o cliente.** + + +```bash +php worker.php > quickstart-worker.log 2>&1 & +export QUICKSTART_WORKER_PID=$! +trap 'kill "$QUICKSTART_WORKER_PID" 2>/dev/null || true' EXIT + +php start.php + +kill "$QUICKSTART_WORKER_PID" 2>/dev/null || true +trap - EXIT +``` + +**Resultado esperado:** `status=completed` e um resultado contendo +`"greeting":"Hello, PHP!"`. Você executou um worker PHP independente e +inspecionou seu resultado durável sem Laravel. + +Continue com o [Guia do SDK PHP](/docs/polyglot/php/). + +
+ + +Requisitos: Python 3.10 ou superior. O programa mantém o worker e o cliente em +um único processo, mas ambos se comunicam com o Server pelas APIs públicas de +workers e do plano de controle. + +1. **Instale o SDK.** + + Use a versão estável do SDK + Python indicada no manifesto acima. + O requisito exato gerado mantém este caminho executável na versão estável + documentada. + + +```bash +mkdir durable-workflow-python-quickstart +cd durable-workflow-python-quickstart + +python3 -m venv .venv +. .venv/bin/activate +pip install %%artifact.pythonPackagePin%% +``` + +2. **Crie e execute o worker e o cliente.** Expanda o programa completo. + Seu último comando executa o programa. + +
+Código Python completo para execução + + +```bash +cat > greeter.py <<'PY' +import asyncio +import time + +from durable_workflow import Client, Worker, activity, workflow + + +@activity.defn(name="quickstart.greet") +async def greet(name: str) -> dict: + return {"greeting": f"Hello, {name}!", "language": "python"} + + +@workflow.defn(name="quickstart.greeter") +class GreeterWorkflow: + def run(self, ctx, name): + return (yield ctx.schedule_activity("quickstart.greet", [name])) + + +async def main(): + workflow_id = f"quickstart-python-greeter-{int(time.time())}" + + async with Client( + "http://localhost:8080", + token="dev-token", + namespace="default", + ) as client: + handle = await client.start_workflow( + workflow_type="quickstart.greeter", + task_queue="quickstart-python", + workflow_id=workflow_id, + input=["Python"], + ) + + worker = Worker( + client, + task_queue="quickstart-python", + workflows=[GreeterWorkflow], + activities=[greet], + ) + await worker.run_until(workflow_id=workflow_id, timeout=30.0) + + result = await handle.result(timeout=10.0) + execution = await handle.describe_run() + + print(f"workflow_id={execution.workflow_id}") + print(f"status={execution.status}") + print(f"result={result}") + + +asyncio.run(main()) +PY + +python greeter.py +``` + +
+ +**Resultado esperado:** `status=completed` e um resultado contendo +`Hello, Python!`. As duas últimas chamadas do SDK leem o resultado da execução +selecionada e seu estado terminal durável no Server. + +Continue com o [Guia do SDK Python](/docs/polyglot/python/). + +
+ + +Requisitos: Rust 1.86 ou superior. Este exemplo executa um worker nativo e um +cliente em um único processo Tokio. + +1. **Instale o SDK.** + + +```bash +cargo new durable-workflow-rust-quickstart +cd durable-workflow-rust-quickstart +%%artifact.rustCargoAddCommand%% +cargo add tokio --features macros,rt-multi-thread,time +``` + +2. **Crie e execute o worker e o cliente.** Expanda o programa completo. + Seu último comando compila e executa o programa. + +
+Código Rust completo para execução + + +```bash +cat > src/main.rs <<'RS' +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use durable_workflow::{json, Client, Result, Worker, WorkflowResultOptions}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::builder("http://localhost:8080") + .token(Some("dev-token".to_string())) + .namespace("default") + .build()?; + let task_queue = "quickstart-rust"; + let mut worker = Worker::new(client.clone(), task_queue); + + worker.register_activity("quickstart.greet", |_context, arguments| async move { + let name = arguments + .get(0) + .and_then(|value| value.as_str()) + .unwrap_or("Rust"); + Ok(json!({"greeting": format!("Hello, {name}!"), "language": "rust"})) + }); + + worker.register_workflow("quickstart.greeter", |context, input| async move { + let name = input.get(0).and_then(|value| value.as_str()).unwrap_or("Rust"); + context.activity("quickstart.greet", json!([name])).await + }); + + worker.register().await?; + let workflow_id = format!("quickstart-rust-greeter-{}", unique_suffix()); + let handle = client + .start_workflow( + "quickstart.greeter", + task_queue, + &workflow_id, + json!(["Rust"]), + ) + .await?; + + let watcher = handle.clone(); + worker + .run_until(async move { + loop { + if watcher.describe().await.is_ok_and(|run| run.is_terminal()) { + break; + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + }) + .await?; + + let result = handle.result(WorkflowResultOptions::default()).await?; + let execution = handle.describe_selected_run().await?; + + println!("workflow_id={workflow_id}"); + println!("status={}", execution.status.as_deref().unwrap_or("unknown")); + println!("result={result}"); + Ok(()) +} + +fn unique_suffix() -> u128 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() +} +RS + +cargo run +``` + +
+ +**Resultado esperado:** `status=completed` e um resultado JSON contendo +`"greeting":"Hello, Rust!"`. O exemplo aguarda o worker concluir a execução, +depois lê o estado durável e o resultado decodificado da execução selecionada. + +Continue com o [Guia do SDK Rust](/docs/polyglot/rust/). + +
+
+ +## 4. Remova o Server local {#4-clean-up-the-local-server} + +Quem usa Cloud não tem um Server local para remover. Para o exercício sob sua +administração: + +```bash +docker rm -f durable-workflow-server +docker volume rm durable-workflow-quickstart +``` + +## Caminho separado: Laravel integrado {#separate-path-embedded-laravel} + +Laravel integrado é um modo oficial de implantação em PHP para aplicações que +querem manter o estado dos workflows, a execução em fila, a configuração e as +ferramentas de operação na infraestrutura Laravel existente. Ele instala +`durable-workflow/workflow`. Não usa Server nem `durable-workflow/sdk`. + +Crie uma aplicação integrada com o pacote publicado: + +```bash +composer create-project laravel/laravel durable-workflow-laravel-quickstart +cd durable-workflow-laravel-quickstart +composer require %%artifact.workflowComposerPackage%% +php artisan migrate +php artisan queue:work +``` + +Para usar a interface de operação dentro da mesma aplicação Laravel, adicione +o pacote Composer qualificado do Waterline integrado: + +```bash +composer require %%artifact.waterlineComposerPackage%% +php artisan waterline:install +``` + +Esse pacote Composer não é a forma de instalar o serviço Waterline separado +para um Server sob sua administração. Laravel integrado não executa Server nem +instala um SDK de modo serviço. + +Continue em [Instalação integrada](/docs/installation/) para configurar uma fila +Laravel diferente de `sync`. Depois, +[defina](/docs/defining-workflows/workflows/) e +[inicie](/docs/defining-workflows/starting-workflows/) um workflow integrado. +[Modos de implantação](/docs/polyglot/deployment-modes/) compara esse caminho +especializado com a plataforma em modo serviço. + +## Próximos passos {#next-steps} + +- Consulte o [Índice de capacidades](/docs/capabilities/) para verificar o que + seu runtime e SDK oferecem. +- Continue com o guia de modo serviço do + [SDK PHP](/docs/polyglot/php/), [SDK Python](/docs/polyglot/python/) ou + [SDK Rust](/docs/polyglot/rust/). +- Compare ciclo de vida, mensagens, agendamentos, visibilidade e execução de + workers em [Capacidades de clientes e workers](/docs/polyglot/cli-python-parity/). +- Opere o runtime escolhido com + [Runtime gerenciado do Cloud](/docs/polyglot/cloud-control-plane/) ou + [Server sob sua administração](/docs/polyglot/server/). Adicione a + [CLI](/docs/polyglot/cli/) quando a automação pelo terminal for útil. +- Planeje a implantação segura de workers em modo serviço com + [Compatibilidade e roteamento de workers](/docs/polyglot/worker-compatibility-routing/) + e [Implantação por build ID](/docs/polyglot/worker-build-id-rollout/). + +Para os recursos de código do Laravel integrado, como temporizadores, sinais, +consultas, atividades e workflows filhos, use a +[Documentação integrada](/docs/category/embedded/). + +A qualificação de releases é separada deste guia de primeiro resultado. +A [Suíte de conformidade da plataforma](/docs/platform-conformance/) reúne +a matriz exata de artefatos, as verificações do código público, os registros +completos de execução, os critérios de tempo, a limpeza e o contrato de +primeiros passos legível por máquina. diff --git a/i18n/pt-BR/source-hashes.json b/i18n/pt-BR/source-hashes.json new file mode 100644 index 0000000000..b9b4cf1a0f --- /dev/null +++ b/i18n/pt-BR/source-hashes.json @@ -0,0 +1,13 @@ +{ + "constraints/activity-constraints.md": "5a953d0e95e750b6c02d1c72b72199fb3ff4687540cebc56aa3b216cf667bb05", + "constraints/execution-guarantees.md": "e0903f1d6d7461191f2190453f9f96ce9bdcf6811e7e99c7d51f81e2f58ef27d", + "constraints/overview.md": "fd6c842c6c22d6f0df37a52de15ca74b34664f5ecd78af0c08b1e0cdb9a2be1f", + "constraints/workflow-constraints.md": "a69c8a118dc790a5d7b6c91806cb7ceca3015004a0fe549aee3094b09cbf17ad", + "defining-workflows/workflows.md": "a0cc26ae208ad3cee612f49e8db41324ee18f700fe67d9f533e48c700df5c386", + "failures-and-recovery.md": "0d2b8c71b7b54a0160c9bd631348a8223c0d05749f7224ec3ea5feaf74a0a3b5", + "how-it-works.md": "5233792ac55aaf1bd80e3cad88fc3ad23e1eda149255611e257459dd918c52e9", + "installation.md": "1e2e04a8f23414d0e81c72b166700fd6350129ab81b2f22a6205856c23741967", + "introduction.md": "06bf96d164d402d4e376c6308b20f96d324110b2e0cf96c2a1a67b39906072e3", + "polyglot/deployment-modes.md": "ee38a2ca890e6ba5d52fffa1fa0bc65e64a1fc82e47af92463983e523d4e3f49", + "quickstart.md": "984de0b5ef14b403a283e597641d9b236e1f3ddb76d460aadaf936ac37be143d" +} diff --git a/i18n/uk/docusaurus-plugin-content-docs/current/how-it-works.md b/i18n/uk/docusaurus-plugin-content-docs/current/how-it-works.md index c168c24935..661cac5e54 100644 --- a/i18n/uk/docusaurus-plugin-content-docs/current/how-it-works.md +++ b/i18n/uk/docusaurus-plugin-content-docs/current/how-it-works.md @@ -28,7 +28,7 @@ Event sourcing відновлює поточний стан із послідо Викликаючи кілька activity, workflow може координувати їхні результати. Виконання workflow чергується зі стійкими кроками, які він планує: workflow доходить до виклику activity, призупиняється до її завершення, а потім продовжує виконання з того самого місця. -У разі збою workflow події до збою відтворюються для відновлення поточного стану. Це дозволяє workflow продовжити з того самого місця з тими самими вхідними даними й результатами, зберігаючи детермінованість. +Якщо worker workflow аварійно завершується, зафіксовані події відтворюються для відновлення поточного стану. Це дозволяє workflow продовжити з того самого місця з тими самими вхідними даними й результатами, зберігаючи детермінованість. Необроблена помилка workflow завершує run у термінальному стані. Replay не повторює невдалий run. Звичайні activity у v2 — стійка робота в черзі. Явні [локальні activity](./features/local-activities.md) виконують коротку роботу @@ -67,7 +67,7 @@ class MyWorkflow extends Workflow return [ activity(TestActivity::class), activity(TestOtherActivity::class), - fn () => all([ + all([ fn () => activity(TestParallelActivity::class), fn () => activity(TestParallelOtherActivity::class), ]), @@ -95,7 +95,7 @@ import ThemedImage from '@site/src/components/ThemedImage'; 3. Workflow входить у цикл відтворення event sourcing. Він повертається до бази даних і читає потік подій для відновлення поточного стану. Це потрібно, бо workflow не є тривалим процесом. Він виходить, поки activity виконуються, та знову надсилається після їх завершення. 4. Після відтворення потоку подій workflow переходить до наступної activity, `TestOtherActivity`, запускаючи її як завдання черги. Після завершення вона також зберігає результат у базі даних і повертає керування workflow, надсилаючи його як завдання черги. 5. Workflow знову входить у цикл відтворення event sourcing та відновлює поточний стан із потоку подій. -6. Далі workflow запускає дві паралельні activity, `TestParallelActivity` та `TestOtherParallelActivity`. Обидві надсилаються. Після завершення вони зберігають результати в базі даних та повертають керування workflow. +6. Далі workflow запускає дві паралельні activity, `TestParallelActivity` та `TestParallelOtherActivity`. Обидві надсилаються. Після завершення вони зберігають результати в базі даних та повертають керування workflow. 7. Нарешті workflow востаннє входить у цикл відтворення event sourcing для відновлення поточного стану з потоку подій. Це завершує виконання workflow. ## Детермінованість {#determinism} diff --git a/scripts/check-doc-translations.js b/scripts/check-doc-translations.js index 912984fb6c..e3c6cbc137 100644 --- a/scripts/check-doc-translations.js +++ b/scripts/check-doc-translations.js @@ -4,42 +4,45 @@ const path = require('node:path'); const {createHash} = require('node:crypto'); const root = path.resolve(__dirname, '..'); -const translatedRoot = path.join(root, 'i18n/es/docusaurus-plugin-content-docs/current'); -const reviewFile = path.join(root, 'i18n/es/source-hashes.json'); -const currentHashes = {}; +const locales = {es: 'Spanish', 'pt-BR': 'Brazilian Portuguese'}; function codeBlocks(file) { const text = fs.readFileSync(file, 'utf8'); return [...text.matchAll(/^```[^\n]*\n[\s\S]*?^```\s*$/gm)].map(match => match[0].trimEnd()); } -let checked = 0; -function visit(directory) { - for (const entry of fs.readdirSync(directory, {withFileTypes: true})) { - const file = path.join(directory, entry.name); - if (entry.isDirectory()) { - visit(file); - } else if (/\.mdx?$/.test(entry.name)) { - const relative = path.relative(translatedRoot, file); - const source = path.join(root, 'docs', relative); - assert.deepEqual(codeBlocks(file), codeBlocks(source), - `Spanish executable examples differ from English: ${relative}`); - currentHashes[relative] = createHash('sha256') - .update(fs.readFileSync(source)).digest('hex'); - checked += 1; +for (const [locale, label] of Object.entries(locales)) { + const translatedRoot = path.join(root, `i18n/${locale}/docusaurus-plugin-content-docs/current`); + const reviewFile = path.join(root, `i18n/${locale}/source-hashes.json`); + const currentHashes = {}; + let checked = 0; + function visit(directory) { + for (const entry of fs.readdirSync(directory, {withFileTypes: true})) { + const file = path.join(directory, entry.name); + if (entry.isDirectory()) { + visit(file); + } else if (/\.mdx?$/.test(entry.name)) { + const relative = path.relative(translatedRoot, file); + const source = path.join(root, 'docs', relative); + assert.deepEqual(codeBlocks(file), codeBlocks(source), + `${label} executable examples differ from English: ${relative}`); + currentHashes[relative] = createHash('sha256') + .update(fs.readFileSync(source)).digest('hex'); + checked += 1; + } } } -} -visit(translatedRoot); -if (process.argv.includes('--record-review')) { - fs.writeFileSync(reviewFile, `${JSON.stringify(currentHashes, null, 2)}\n`); -} else { - const reviewedHashes = JSON.parse(fs.readFileSync(reviewFile, 'utf8')); - const changedSources = Object.keys(currentHashes) - .filter(file => currentHashes[file] !== reviewedHashes[file]); - if (changedSources.length) { - console.warn(`Spanish translation review needed: ${changedSources.join(', ')}. See README.md.`); + visit(translatedRoot); + if (process.argv.includes('--record-review')) { + fs.writeFileSync(reviewFile, `${JSON.stringify(currentHashes, null, 2)}\n`); + } else { + const reviewedHashes = JSON.parse(fs.readFileSync(reviewFile, 'utf8')); + const changedSources = Object.keys(currentHashes) + .filter(file => currentHashes[file] !== reviewedHashes[file]); + if (changedSources.length) { + console.warn(`${label} translation review needed: ${changedSources.join(', ')}. See README.md.`); + } } + console.log(`${label} code blocks match English in ${checked} guides.`); } -console.log(`Spanish code blocks match English in ${checked} guides.`);