@seedgrid/ai-toolkit 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +151 -2
- package/dist/adapters/claude.js +114 -0
- package/dist/adapters/codex.js +108 -0
- package/dist/adapters/index.js +17 -0
- package/dist/adapters/shared.js +142 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli.js +203 -0
- package/dist/commands/common.js +98 -0
- package/dist/commands/doctor.js +290 -0
- package/dist/commands/init.js +9 -0
- package/dist/commands/install.js +107 -0
- package/dist/commands/remove.js +54 -0
- package/dist/commands/review.js +127 -0
- package/dist/commands/status.js +73 -0
- package/dist/commands/update.js +8 -0
- package/dist/commands/validate.js +27 -0
- package/dist/core/apply.js +181 -0
- package/dist/core/blocks.js +64 -0
- package/dist/core/content.js +528 -0
- package/dist/core/env.js +40 -0
- package/dist/core/frontmatter.js +29 -0
- package/dist/core/hash.js +14 -0
- package/dist/core/inspect.js +51 -0
- package/dist/core/lock.js +124 -0
- package/dist/core/paths.js +65 -0
- package/dist/core/plan.js +242 -0
- package/dist/core/tools.js +62 -0
- package/dist/core/types.js +11 -0
- package/dist/core/updates.js +121 -0
- package/docs/arquitetura.md +80 -0
- package/docs/compatibilidade.md +50 -0
- package/docs/formato.md +155 -0
- package/docs/instalacao.md +105 -0
- package/docs/manutencao.md +115 -0
- package/package.json +43 -4
- package/seedgridKB/SeedGrid/Extensions/Catalogo-de-enums.md +161 -0
- package/seedgridKB/SeedGrid/Extensions/Catalogo-de-propriedades.md +971 -0
- package/seedgridKB/SeedGrid/Extensions/Indice.md +63 -0
- package/seedgridKB/SeedGrid/Extensions/ai-pricing/Configuracao.md +124 -0
- package/seedgridKB/SeedGrid/Extensions/ai-pricing/Enums.md +40 -0
- package/seedgridKB/SeedGrid/Extensions/ai-pricing/Exemplos.md +97 -0
- package/seedgridKB/SeedGrid/Extensions/ai-pricing/Visao-geral.md +114 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Configuracao-Rust.md +69 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Configuracao.md +88 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Enums.md +33 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Exemplos.md +121 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Rust.md +126 -0
- package/seedgridKB/SeedGrid/Extensions/apikeys/Visao-geral.md +111 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Configuracao-Rust.md +93 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Configuracao.md +127 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Enums.md +66 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Exemplos.md +151 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Rust.md +120 -0
- package/seedgridKB/SeedGrid/Extensions/audit/Visao-geral.md +114 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Configuracao-Rust.md +136 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Configuracao.md +319 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Enums.md +131 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Exemplos.md +210 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Rust.md +135 -0
- package/seedgridKB/SeedGrid/Extensions/backup/Visao-geral.md +150 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Configuracao-Rust.md +86 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Configuracao.md +243 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Enums.md +165 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Exemplos.md +205 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Rust.md +109 -0
- package/seedgridKB/SeedGrid/Extensions/billing/Visao-geral.md +181 -0
- package/seedgridKB/SeedGrid/Extensions/broker/Configuracao-Rust.md +40 -0
- package/seedgridKB/SeedGrid/Extensions/broker/Visao-geral.md +114 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Configuracao-Rust.md +73 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Configuracao.md +112 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Enums.md +36 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Exemplos.md +149 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Rust.md +81 -0
- package/seedgridKB/SeedGrid/Extensions/cache/Visao-geral.md +90 -0
- package/seedgridKB/SeedGrid/Extensions/commons/Configuracao.md +107 -0
- package/seedgridKB/SeedGrid/Extensions/commons/Enums.md +105 -0
- package/seedgridKB/SeedGrid/Extensions/commons/Exemplos.md +141 -0
- package/seedgridKB/SeedGrid/Extensions/commons/Visao-geral.md +99 -0
- package/seedgridKB/SeedGrid/Extensions/exchange/Configuracao.md +122 -0
- package/seedgridKB/SeedGrid/Extensions/exchange/Enums.md +29 -0
- package/seedgridKB/SeedGrid/Extensions/exchange/Exemplos.md +82 -0
- package/seedgridKB/SeedGrid/Extensions/exchange/Visao-geral.md +118 -0
- package/seedgridKB/SeedGrid/Extensions/filemanagement/Configuracao-Rust.md +110 -0
- package/seedgridKB/SeedGrid/Extensions/filemanagement/Configuracao.md +157 -0
- package/seedgridKB/SeedGrid/Extensions/filemanagement/Exemplos.md +157 -0
- package/seedgridKB/SeedGrid/Extensions/filemanagement/Rust.md +70 -0
- package/seedgridKB/SeedGrid/Extensions/filemanagement/Visao-geral.md +88 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Configuracao-Rust.md +124 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Configuracao.md +195 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Enums.md +71 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Exemplos.md +149 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Rust.md +139 -0
- package/seedgridKB/SeedGrid/Extensions/login-tenancy/Visao-geral.md +130 -0
- package/seedgridKB/SeedGrid/Extensions/notification/Configuracao.md +141 -0
- package/seedgridKB/SeedGrid/Extensions/notification/Enums.md +52 -0
- package/seedgridKB/SeedGrid/Extensions/notification/Exemplos.md +141 -0
- package/seedgridKB/SeedGrid/Extensions/notification/Visao-geral.md +113 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Configuracao-Rust.md +67 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Configuracao.md +94 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Enums.md +79 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Exemplos.md +172 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Rust.md +107 -0
- package/seedgridKB/SeedGrid/Extensions/privacy/Visao-geral.md +90 -0
- package/seedgridKB/SeedGrid/Extensions/realtime/Configuracao-Rust.md +72 -0
- package/seedgridKB/SeedGrid/Extensions/realtime/Configuracao.md +104 -0
- package/seedgridKB/SeedGrid/Extensions/realtime/Exemplos.md +104 -0
- package/seedgridKB/SeedGrid/Extensions/realtime/Rust.md +69 -0
- package/seedgridKB/SeedGrid/Extensions/realtime/Visao-geral.md +105 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Configuracao-Rust.md +68 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Configuracao.md +186 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Enums.md +96 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Exemplos.md +229 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Rust.md +80 -0
- package/seedgridKB/SeedGrid/Extensions/resilience/Visao-geral.md +112 -0
- package/seedgridKB/SeedGrid/Extensions/runtime/Configuracao-Rust.md +90 -0
- package/seedgridKB/SeedGrid/Extensions/runtime/Visao-geral.md +114 -0
- package/seedgridKB/SeedGrid/Extensions/security/Configuracao-Rust.md +327 -0
- package/seedgridKB/SeedGrid/Extensions/security/Configuracao.md +510 -0
- package/seedgridKB/SeedGrid/Extensions/security/Enums.md +95 -0
- package/seedgridKB/SeedGrid/Extensions/security/Exemplos.md +218 -0
- package/seedgridKB/SeedGrid/Extensions/security/Rust.md +203 -0
- package/seedgridKB/SeedGrid/Extensions/security/Visao-geral.md +131 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Configuracao-Rust.md +188 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Configuracao.md +345 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Enums.md +117 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Exemplos.md +205 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Rust.md +161 -0
- package/seedgridKB/SeedGrid/Extensions/subdomain-tenancy/Visao-geral.md +142 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Configuracao-Rust.md +75 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Configuracao.md +152 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Enums.md +198 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Exemplos.md +173 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Rust.md +83 -0
- package/seedgridKB/SeedGrid/Extensions/whatsapp/Visao-geral.md +99 -0
- package/seedgridKB/SeedGrid/Mapa-dos-repositorios.md +112 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/Indice.md +323 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/buttons/API.md +257 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/buttons/Exemplos.md +307 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/buttons/Visao-geral.md +51 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/datatable/API.md +174 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/datatable/Exemplos.md +236 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/datatable/Visao-geral.md +76 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/digits/API.md +339 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/digits/Exemplos.md +311 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/digits/Visao-geral.md +57 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/editor/API.md +125 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/editor/Exemplos.md +116 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/editor/Visao-geral.md +86 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/feedback-e-exibicao/API.md +490 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/feedback-e-exibicao/Exemplos.md +544 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/feedback-e-exibicao/Visao-geral.md +69 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/fundamentos/API.md +113 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/fundamentos/Exemplos.md +159 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/fundamentos/Visao-geral.md +92 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/gadgets/API.md +516 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/gadgets/Exemplos.md +629 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/gadgets/Visao-geral.md +80 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-selecao/API.md +615 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-selecao/Exemplos.md +384 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-selecao/Visao-geral.md +111 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-texto/API.md +661 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-texto/Exemplos.md +312 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-texto/Visao-geral.md +123 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/layout/API.md +1090 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/layout/Exemplos.md +1172 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/layout/Visao-geral.md +98 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/Visao-geral.md +98 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-apikeys.md +118 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-audit.md +88 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-backup.md +131 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-billing-platform.md +132 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-billing-user.md +120 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-login-tenancy.md +119 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-security.md +427 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-self-enter.md +104 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/modulos/fe-subdomain-tenancy.md +110 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/navegacao-e-overlay/API.md +490 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/navegacao-e-overlay/Exemplos.md +561 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/navegacao-e-overlay/Visao-geral.md +82 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/pacotes/Visao-geral.md +68 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/pacotes/fe-commons.md +61 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/pacotes/fe-core.md +140 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/pacotes/fe-playground.md +112 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/pacotes/fe-theme.md +97 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/providers/API.md +177 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/providers/Exemplos.md +332 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/providers/Visao-geral.md +106 -0
- package/seedgridKB/SeedGrid/SeedGrid-components/showcase.md +70 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/arquiteto.md +53 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/backend-java.md +45 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/backend-rust.md +43 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/frontend-react.md +41 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/qa.md +44 -0
- package/seedgridKB/SeedGrid/Toolkit/agents/revisor.md +40 -0
- package/seedgridKB/SeedGrid/Toolkit/references/fluxo-de-trabalho.md +78 -0
- package/seedgridKB/SeedGrid/Toolkit/references/glossario.md +15 -0
- package/seedgridKB/SeedGrid/Toolkit/references/modos-shared-native.md +27 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/backend-java-seedgrid.md +54 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/coordenacao.md +39 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/formato-entrega.md +29 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/frontend-seedgrid.md +47 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/personalizacao-local.md +22 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/planejar-mudanca/SKILL.md +38 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/revisar-codigo/SKILL.md +33 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/revisar-codigo/references/checklist.md +25 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-backend-crud/SKILL.md +29 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-bump/SKILL.md +26 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-crud/SKILL.md +221 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-crud/agents/openai.yaml +4 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-crud/references/checklist.md +31 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-crud/references/patterns.md +185 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-crud/references/routing.md +66 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-new-screen/SKILL.md +56 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-new-screen/agents/openai.yaml +4 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-new-screen/references/checklist.md +32 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-new-screen/references/steps.md +114 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-review/SKILL.md +61 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/validar-mudanca/SKILL.md +29 -0
- package/seedgridKB/SeedGrid/Toolkit/templates/decisao-arquitetura.md +29 -0
- package/seedgridKB/SeedGrid/Toolkit/templates/especificacao-tarefa.md +32 -0
- package/seedgridKB/SeedGrid/Toolkit/templates/relatorio-validacao.md +30 -0
- package/seedgridKB/SeedGrid/Versoes.md +64 -0
- package/seedgridKB/SeedGrid/Visao-geral.md +35 -0
- package/seedgridKB/manifest.json +142 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, extension, resilience, exemplos]
|
|
3
|
+
fonte: java/seedgrid-quarkus-ext-resilience
|
|
4
|
+
branch: main
|
|
5
|
+
commit: eff41304f8
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Exemplos — SeedGrid Resilience (Quarkus)
|
|
10
|
+
|
|
11
|
+
Voltar: [[SeedGrid/Extensions/resilience/Visao-geral|Visão geral do resilience]] · Propriedades: [[SeedGrid/Extensions/resilience/Configuracao|Configuração do resilience]]
|
|
12
|
+
|
|
13
|
+
Pacote base: `br.com.seedgrid.resilience.core.runtime`. Exemplos baseados em `README.md`, `README.adoc` e nas assinaturas reais das anotações/SPIs.
|
|
14
|
+
|
|
15
|
+
## Dependência
|
|
16
|
+
|
|
17
|
+
```xml
|
|
18
|
+
<dependency>
|
|
19
|
+
<groupId>br.com.seedgrid</groupId>
|
|
20
|
+
<artifactId>seedgrid-resilience-core</artifactId>
|
|
21
|
+
<version>2026.10.13</version>
|
|
22
|
+
</dependency>
|
|
23
|
+
<!-- estado distribuído: um provider de cache (ex.: Redis) -->
|
|
24
|
+
<dependency>
|
|
25
|
+
<groupId>br.com.seedgrid</groupId>
|
|
26
|
+
<artifactId>seedgrid-cache-redis</artifactId>
|
|
27
|
+
<version>2026.10.13</version>
|
|
28
|
+
</dependency>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Versão `2026.10.13` = pom atual de `seedgrid-quarkus-ext-resilience` e de `seedgrid-quarkus-lib-cache`. **Confirmado**.
|
|
32
|
+
|
|
33
|
+
## 1. Rate limit básico (por IP)
|
|
34
|
+
|
|
35
|
+
Fonte: `README.md`.
|
|
36
|
+
|
|
37
|
+
```java
|
|
38
|
+
import java.time.temporal.ChronoUnit;
|
|
39
|
+
import br.com.seedgrid.resilience.core.runtime.ratelimiter.annotations.SeedGridRateLimit;
|
|
40
|
+
|
|
41
|
+
@Path("/auth")
|
|
42
|
+
public class SignupResource {
|
|
43
|
+
|
|
44
|
+
@POST
|
|
45
|
+
@Path("/signup")
|
|
46
|
+
@SeedGridRateLimit(permits = 5, window = 1, windowUnit = ChronoUnit.MINUTES)
|
|
47
|
+
public Response signup(SignupRequest req) { ... } // 5 req/min por IP
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Ao estourar: HTTP 429 com `X-RateLimit-Limit`, `X-RateLimit-Remaining: 0`, `X-RateLimit-Reset` e `Retry-After` (via `RateLimitExceptionMapper`).
|
|
52
|
+
|
|
53
|
+
## 2. Limiters independentes ajustáveis por properties
|
|
54
|
+
|
|
55
|
+
Fonte: `README.md`.
|
|
56
|
+
|
|
57
|
+
```java
|
|
58
|
+
@SeedGridRateLimit(configKey = "login", permits = 100, window = 1, windowUnit = ChronoUnit.MINUTES)
|
|
59
|
+
public AuthResponseDTO login(LoginDTO dto) { ... }
|
|
60
|
+
|
|
61
|
+
@SeedGridRateLimit(configKey = "export-create", permits = 10, window = 1, windowUnit = ChronoUnit.HOURS)
|
|
62
|
+
public Response createExport(ExportRequest req) { ... }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```properties
|
|
66
|
+
seedgrid.ratelimit.login.permits=200
|
|
67
|
+
seedgrid.ratelimit.login.window=PT30S
|
|
68
|
+
seedgrid.ratelimit.export-create.algorithm=token-bucket
|
|
69
|
+
seedgrid.ratelimit.export-create.burst-capacity=3
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 3. Token bucket com rajada e espaçamento mínimo
|
|
73
|
+
|
|
74
|
+
Fonte: `README.adoc` § "Exemplo de Token Bucket".
|
|
75
|
+
|
|
76
|
+
```java
|
|
77
|
+
import br.com.seedgrid.resilience.core.runtime.ratelimiter.enums.RateLimitAlgorithm;
|
|
78
|
+
|
|
79
|
+
@SeedGridRateLimit(
|
|
80
|
+
permits = 10,
|
|
81
|
+
window = 1,
|
|
82
|
+
windowUnit = ChronoUnit.MINUTES,
|
|
83
|
+
algorithm = RateLimitAlgorithm.TOKEN_BUCKET,
|
|
84
|
+
burstCapacity = 20,
|
|
85
|
+
minSpacing = 200,
|
|
86
|
+
minSpacingUnit = ChronoUnit.MILLIS)
|
|
87
|
+
public void syncProducts() { ... }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`minSpacing` não tem efeito com `FIXED_WINDOW` (ver [[SeedGrid/Extensions/resilience/Visao-geral|Visão geral]]).
|
|
91
|
+
|
|
92
|
+
## 4. Cota compartilhada entre métodos (`GROUP`)
|
|
93
|
+
|
|
94
|
+
Fonte: `README.adoc` § "Compartilhando cota com GROUP". Observe que `KeyScope` fica no pacote `circuitbreaker.enums`.
|
|
95
|
+
|
|
96
|
+
```java
|
|
97
|
+
import br.com.seedgrid.resilience.core.runtime.circuitbreaker.enums.KeyScope;
|
|
98
|
+
|
|
99
|
+
@SeedGridRateLimit(permits = 300, window = 1, windowUnit = ChronoUnit.MINUTES,
|
|
100
|
+
group = "email-provider", scope = KeyScope.GROUP)
|
|
101
|
+
public void sendWelcomeEmail(...) { ... }
|
|
102
|
+
|
|
103
|
+
@SeedGridRateLimit(permits = 300, window = 1, windowUnit = ChronoUnit.MINUTES,
|
|
104
|
+
group = "email-provider", scope = KeyScope.GROUP)
|
|
105
|
+
public void sendResetEmail(...) { ... }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 5. Contexto sem HTTP (jobs, consumers)
|
|
109
|
+
|
|
110
|
+
Fonte: Javadoc de `SeedGridRateLimit#identity` e `README.adoc`.
|
|
111
|
+
|
|
112
|
+
```java
|
|
113
|
+
import br.com.seedgrid.resilience.core.runtime.ratelimiter.spi.AnonymousIdentityResolver;
|
|
114
|
+
|
|
115
|
+
@SeedGridRateLimit(permits = 10, window = 1, windowUnit = ChronoUnit.MINUTES,
|
|
116
|
+
identity = AnonymousIdentityResolver.class)
|
|
117
|
+
public void consume(Message msg) { ... }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## 6. Identidade customizada (SPI `IdentityResolver`)
|
|
121
|
+
|
|
122
|
+
Fonte: `README.md`. Assinatura real: `String resolve(SeedGridInvocationContext ctx)`; nunca retornar `null`.
|
|
123
|
+
|
|
124
|
+
```java
|
|
125
|
+
import br.com.seedgrid.resilience.core.runtime.core.SeedGridInvocationContext;
|
|
126
|
+
import br.com.seedgrid.resilience.core.runtime.ratelimiter.spi.IdentityResolver;
|
|
127
|
+
|
|
128
|
+
@ApplicationScoped
|
|
129
|
+
public class TenantIdentityResolver implements IdentityResolver {
|
|
130
|
+
@Override
|
|
131
|
+
public String resolve(SeedGridInvocationContext ctx) {
|
|
132
|
+
return ctx.tenantId().orElse("anonymous");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@SeedGridRateLimit(permits = 1000, window = 1, windowUnit = ChronoUnit.HOURS,
|
|
137
|
+
identity = TenantIdentityResolver.class)
|
|
138
|
+
public Report generate(...) { ... }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
O `RateLimitService` busca o resolver via `CDI.current().select(classe)`; se não for bean resolvível, instancia pelo construtor sem argumentos.
|
|
142
|
+
|
|
143
|
+
## 7. Publicando tenant/operação no contexto
|
|
144
|
+
|
|
145
|
+
A chave do rate limit inclui os atributos `tenantId` e `operation` do `SeedGridInvocationContext` corrente. A extension não preenche esse contexto; o host pode usar `ThreadLocalSeedGridInvocationContext` (API real: `set`, `clear`, `runWith`, `callWith`). Implementação ilustrativa do contexto (a interface exige `header`, `method`, `path`, `remoteIp`, `attribute`, `tags`):
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
import java.util.Map;
|
|
149
|
+
import java.util.Optional;
|
|
150
|
+
import br.com.seedgrid.resilience.core.runtime.core.SeedGridInvocationContext;
|
|
151
|
+
import br.com.seedgrid.resilience.core.runtime.core.ThreadLocalSeedGridInvocationContext;
|
|
152
|
+
|
|
153
|
+
record JobContext(Map<String, Object> attrs) implements SeedGridInvocationContext {
|
|
154
|
+
public Optional<String> header(String name) { return Optional.empty(); }
|
|
155
|
+
public Optional<String> method() { return Optional.empty(); }
|
|
156
|
+
public Optional<String> path() { return Optional.empty(); }
|
|
157
|
+
public Optional<String> remoteIp() { return Optional.empty(); }
|
|
158
|
+
public Optional<Object> attribute(String name) { return Optional.ofNullable(attrs.get(name)); }
|
|
159
|
+
public Optional<Map<String, String>> tags() { return Optional.empty(); }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
ThreadLocalSeedGridInvocationContext.runWith(
|
|
163
|
+
new JobContext(Map.of("tenantId", "acme", "operation", "sync-products")),
|
|
164
|
+
() -> productSync.syncProducts());
|
|
165
|
+
// chave resultante: rl:acme:sync-products:<escopo>:<identidade>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 8. Circuit breaker com fallback
|
|
169
|
+
|
|
170
|
+
Fonte: `README.md`.
|
|
171
|
+
|
|
172
|
+
```java
|
|
173
|
+
import br.com.seedgrid.resilience.core.runtime.circuitbreaker.annotations.SeedGridCircuitBreaker;
|
|
174
|
+
|
|
175
|
+
@ApplicationScoped
|
|
176
|
+
public class PaymentGateway {
|
|
177
|
+
|
|
178
|
+
@SeedGridCircuitBreaker(
|
|
179
|
+
name = "payments",
|
|
180
|
+
failureRateThreshold = 50.0,
|
|
181
|
+
minimumNumberOfCalls = 20,
|
|
182
|
+
openStateDurationMs = 30_000,
|
|
183
|
+
permittedCallsInHalfOpen = 10,
|
|
184
|
+
fallbackMethod = "paymentsFallback")
|
|
185
|
+
public PaymentResult charge(Order order) { ... }
|
|
186
|
+
|
|
187
|
+
// precisa ser público; procurado como (params..., Exceção), (params...) ou ()
|
|
188
|
+
public PaymentResult paymentsFallback(Order order, Exception cause) { ... }
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Sem fallback, circuito aberto lança `CircuitBreakerOpenException` (`circuitName()`, `retryAfterMs()`), que **não** tem `ExceptionMapper` na extension. Exemplo de mapper no host (usa só a API pública da exceção):
|
|
193
|
+
|
|
194
|
+
```java
|
|
195
|
+
import br.com.seedgrid.resilience.core.runtime.circuitbreaker.exceptions.CircuitBreakerOpenException;
|
|
196
|
+
|
|
197
|
+
@Provider
|
|
198
|
+
public class CircuitOpenMapper implements ExceptionMapper<CircuitBreakerOpenException> {
|
|
199
|
+
@Override
|
|
200
|
+
public Response toResponse(CircuitBreakerOpenException ex) {
|
|
201
|
+
return Response.status(503)
|
|
202
|
+
.header("Retry-After", (ex.retryAfterMs() + 999) / 1000)
|
|
203
|
+
.build();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 9. Circuit breaker com slow calls e exceções filtradas
|
|
209
|
+
|
|
210
|
+
```java
|
|
211
|
+
import br.com.seedgrid.resilience.core.runtime.circuitbreaker.enums.SlidingWindowType;
|
|
212
|
+
|
|
213
|
+
@SeedGridCircuitBreaker(
|
|
214
|
+
name = "erp",
|
|
215
|
+
windowType = SlidingWindowType.TIME_BASED,
|
|
216
|
+
slidingWindowSize = 60, // em TIME_BASED: 60 segundos (e até 60 registros)
|
|
217
|
+
slowCallDurationThresholdMs = 2_000,
|
|
218
|
+
slowCallRateThreshold = 80.0,
|
|
219
|
+
recordExceptions = { java.io.IOException.class },
|
|
220
|
+
ignoreExceptions = { IllegalArgumentException.class })
|
|
221
|
+
public ErpResponse fetch(String id) { ... }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Override em produção:
|
|
225
|
+
|
|
226
|
+
```properties
|
|
227
|
+
seedgrid.circuitbreaker.erp.slow-call-duration=PT3S
|
|
228
|
+
seedgrid.circuitbreaker.erp.enabled=false # kill switch
|
|
229
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, extension, resilience, rust]
|
|
3
|
+
fonte: rust/seedgrid-rust-crate-resilience
|
|
4
|
+
branch: main
|
|
5
|
+
commit: be738a2d1e
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# resilience — porte Rust (`seedgrid-rust-crate-resilience`)
|
|
10
|
+
|
|
11
|
+
Porte da extensão Quarkus `seedgrid-quarkus-ext-resilience`. Lado Java: [[SeedGrid/Extensions/resilience/Visao-geral|Visão geral do resilience (Java)]] · [[SeedGrid/Extensions/resilience/Configuracao|Configuração do resilience (Java)]]. Configuração Rust: [[SeedGrid/Extensions/resilience/Configuracao-Rust|Configuração Rust do resilience]].
|
|
12
|
+
|
|
13
|
+
Entrega **apenas** Circuit Breaker e Rate Limiter, ambos guardando estado num `SeedGridCacheProvider` (in-memory ou distribuído). Não há retry, timeout nem bulkhead. **Confirmado** (`src/lib.rs`).
|
|
14
|
+
|
|
15
|
+
## Identificação
|
|
16
|
+
| Item | Valor |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Pacote Cargo | `seedgrid-rust-crate-resilience` (lib `seedgrid_resilience`) |
|
|
19
|
+
| Versão | workspace `2026.10.14` |
|
|
20
|
+
| Features | nenhuma |
|
|
21
|
+
| Dependência SeedGrid | `seedgrid-rust-crate-cache` |
|
|
22
|
+
| Consumidores | security (`src/ratelimit.rs`), backup e billing (seams de rate limit), `seedgrid-runtime` (macros `#[rate_limit]`/`#[circuit_breaker]`) |
|
|
23
|
+
|
|
24
|
+
## Arquitetura
|
|
25
|
+
No Java, CB e rate limit são interceptors CDI (`@SeedGridCircuitBreaker`, `@SeedGridRateLimit`). Em Rust:
|
|
26
|
+
- **Circuit Breaker**: `circuit_breaker(name, CircuitBreakerConfig, cache)` devolve uma guarda `CircuitBreaker` com `call(fut)` / `call_with_fallback(fut, fallback)`; estado em `CacheBackedCircuitBreakerStore` (chave `cb:<name>`). Config efetiva por `CircuitBreakerConfigResolver::resolve(&SeedGridCircuitBreaker, nome)`.
|
|
27
|
+
- **Rate Limiter**: `RateLimitService::new(cache, Arc<dyn ConfigSource>)` + `try_acquire(&SeedGridRateLimit, &InvocationInfo, ctx)`; algoritmos `FIXED_WINDOW`, `SLIDING_WINDOW`, `TOKEN_BUCKET`; chave `rl:<tenant>[:<operação>]:<escopo>:<identidade>`. Para Axum: `ServiceRateLimiter` + `rate_limit_middleware` (429 + headers `X-RateLimit-*`).
|
|
28
|
+
- Ordem: rate limit é a camada mais externa (prioridade Java 1000), circuit breaker por dentro (1010).
|
|
29
|
+
- As macros de `seedgrid-runtime` (`#[rate_limit]`, `#[circuit_breaker]`) geram essa fiação — ver [[SeedGrid/Extensions/runtime/Visao-geral|Visão geral do runtime]].
|
|
30
|
+
|
|
31
|
+
## API pública
|
|
32
|
+
| Módulo | Símbolos |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `config` | `ConfigSource` (trait tipada: `get_bool/long/int/double/duration/string`), `EmptyConfigSource`, `MapConfigSource`, `ConfigValue` |
|
|
35
|
+
| `circuitbreaker` | `SeedGridCircuitBreaker`, `CircuitBreakerConfig` (+ builder), `CircuitBreakerConfigResolver`, `CircuitBreakerStore`, `CacheBackedCircuitBreakerStore`, `circuit_breaker`, `CircuitBreaker`, `AsErrorClass`, `ErrorClass`, `CircuitBreakerOpenException`, `SlidingWindowType` |
|
|
36
|
+
| `ratelimiter` | `SeedGridRateLimit`, `InvocationInfo`, `RateLimitService`, `Clock`, `RateLimiter`, `ServiceRateLimiter`, `rate_limit_middleware`, `apply_rate_limit_headers`, `IdentityResolver` (`IpIdentityResolver` — usa `X-Forwarded-For`/`X-Real-IP`; `AnonymousIdentityResolver`), `SeedGridInvocationContext`, `MapInvocationContext`, `RateLimitExceededException`, `RateLimitAlgorithm`, `KeyScope` (`Method`, `Class`, `Group`), `TimeUnit` |
|
|
37
|
+
|
|
38
|
+
## Exemplos
|
|
39
|
+
Circuit breaker (doc de `src/circuitbreaker/guard.rs`):
|
|
40
|
+
```rust
|
|
41
|
+
let cache = Arc::new(InMemoryCache::new());
|
|
42
|
+
let cfg = CircuitBreakerConfig::builder().name("backup-upload").build();
|
|
43
|
+
let cb = circuit_breaker("backup-upload", cfg, cache);
|
|
44
|
+
let r: Result<u8, _> = cb.call(async { Ok::<u8, MinhaFalha>(1) }).await;
|
|
45
|
+
```
|
|
46
|
+
Rate limit numa rota Axum (padrão usado por backup/billing; seams `export_create_rate_limit`, `checkout_rate_limit`…):
|
|
47
|
+
```rust
|
|
48
|
+
let service = Arc::new(RateLimitService::new(cache, Arc::new(EmptyConfigSource)));
|
|
49
|
+
let limiter: Arc<dyn RateLimiter> = Arc::new(ServiceRateLimiter::new(
|
|
50
|
+
service, seedgrid_billing::routes::checkout_rate_limit(), InvocationInfo::new("POST /billing/checkout", "billing")));
|
|
51
|
+
let route = route.layer(axum::middleware::from_fn_with_state(limiter, rate_limit_middleware));
|
|
52
|
+
```
|
|
53
|
+
(O segundo bloco é **Inferência** montada a partir das assinaturas de `ServiceRateLimiter::new` e do doc de `rate_limit_middleware`; não há esse trecho literal no repositório.) Exemplo executável com macros: `rust/seedgrid-runtime/examples/resilience_audit.rs`.
|
|
54
|
+
|
|
55
|
+
## Testes
|
|
56
|
+
`tests/circuit_breaker_config_resolver.rs`, `circuit_breaker_store.rs`, `http_headers.rs`, `identity_resolver.rs`, `rate_limit_algorithms.rs`, `rate_limit_service_config_override.rs` (+ unitários). **Não executados.** Sem app em `rust/examples/`.
|
|
57
|
+
|
|
58
|
+
## Notas de comportamento
|
|
59
|
+
|
|
60
|
+
Valores inválidos de `algorithm`/`window-type` e config inválida (`permits <= 0`, `window <= 0`) causam **panic**.
|
|
61
|
+
|
|
62
|
+
## Enums
|
|
63
|
+
Java: [[SeedGrid/Extensions/resilience/Enums|Enums do resilience (Java)]].
|
|
64
|
+
|
|
65
|
+
| Enum | Arquivo | Java equivalente |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `RateLimitAlgorithm` | `src/ratelimiter/enums.rs` | [[SeedGrid/Extensions/resilience/Enums#RateLimitAlgorithm\|RateLimitAlgorithm]] |
|
|
68
|
+
| `KeyScope` | idem | [[SeedGrid/Extensions/resilience/Enums#KeyScope\|KeyScope]] |
|
|
69
|
+
| `TimeUnit` | idem | `java.time.temporal.ChronoUnit` (subconjunto) |
|
|
70
|
+
| `CircuitState` | `src/circuitbreaker/enums.rs` | [[SeedGrid/Extensions/resilience/Enums#CircuitState\|CircuitState]] |
|
|
71
|
+
| `SlidingWindowType` | idem | [[SeedGrid/Extensions/resilience/Enums#SlidingWindowType\|SlidingWindowType]] |
|
|
72
|
+
| `CircuitBreakerError<E>` (`Open`, `Inner`) | `src/circuitbreaker/guard.rs` | exceções do circuito |
|
|
73
|
+
| `ConfigValue` (`Bool`, `Long`, `Int`, `Double`, `Duration`, `Str`) | `src/config.rs` | tipo do valor numa `MapConfigSource` |
|
|
74
|
+
|
|
75
|
+
Nenhum tem serde. Os nomes em texto vêm da `ConfigSource` (normalizados por trim, maiúsculas, `-` → `_`).
|
|
76
|
+
- **RateLimitAlgorithm**: `FixedWindow` (`FIXED_WINDOW`, padrão), `TokenBucket` (`TOKEN_BUCKET`), `SlidingWindow` (`SLIDING_WINDOW`). Valor desconhecido na config → *panic*. Iguais ao Java.
|
|
77
|
+
- **KeyScope**: `Method` (padrão), `Class`, `Group` (cai na assinatura do método se `group` vazio). Só definido em código (não há override por config).
|
|
78
|
+
- **TimeUnit**: `Millis`, `Seconds`, `Minutes`, `Hours`. Nota: o Java aceita qualquer `ChronoUnit`; o Rust só estes 4.
|
|
79
|
+
- **CircuitState**: `Closed` (0), `Open` (1), `HalfOpen` (2). Os ordinais entram na serialização binária do estado no cache (`ordinal`/`from_ordinal`; ordinal inválido → `Closed`). Iguais ao Java.
|
|
80
|
+
- **SlidingWindowType**: `CountBased` (padrão), `TimeBased` (o tamanho da janela vira segundos). A config `window-type` aceita `COUNT_BASED`/`TIME_BASED` (inválido → *panic*).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, extension, resilience]
|
|
3
|
+
fonte: java/seedgrid-quarkus-ext-resilience
|
|
4
|
+
branch: main
|
|
5
|
+
commit: eff41304f8
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# SeedGrid Resilience (Quarkus) — Visão geral
|
|
10
|
+
|
|
11
|
+
Extension Quarkus de **rate limiting** e **circuit breaker** declarativos. O desenvolvedor anota métodos/classes CDI com `@SeedGridRateLimit` ou `@SeedGridCircuitBreaker`; interceptors CDI aplicam a regra e guardam o estado no `SeedGridCacheProvider` (lib `seedgrid-cache`), de modo que, com um provider distribuído (Redis/Valkey/Dragonfly), o limite/circuito é compartilhado entre instâncias. A anotação define os valores padrão; com `configKey` (rate limit) ou `name` (circuit breaker) os valores podem ser sobrescritos por properties em runtime.
|
|
12
|
+
|
|
13
|
+
- Configuração completa: [[SeedGrid/Extensions/resilience/Configuracao|Configuração do resilience]]
|
|
14
|
+
- Enums (`RateLimitAlgorithm`, `KeyScope`, `SlidingWindowType`, `CircuitState`): [[SeedGrid/Extensions/resilience/Enums|Enums do resilience]]
|
|
15
|
+
- Exemplos: [[SeedGrid/Extensions/resilience/Exemplos|Exemplos do resilience]]
|
|
16
|
+
- Crate equivalente: [[SeedGrid/Extensions/resilience/Rust|Porte Rust]]
|
|
17
|
+
- Dependência de cache: [[SeedGrid/Extensions/cache/Visao-geral|Visão geral do cache]]
|
|
18
|
+
|
|
19
|
+
> Não é baseada em MicroProfile Fault Tolerance / SmallRye Fault Tolerance: não há dependência `quarkus-smallrye-fault-tolerance` nem uso de `@Retry`/`@CircuitBreaker` do MP. Propriedades no formato `<classe>/<método>/<Anotação>/<param>` **não** se aplicam. **Confirmado** (poms e imports).
|
|
20
|
+
|
|
21
|
+
## Módulos e coordenadas
|
|
22
|
+
|
|
23
|
+
| Módulo | groupId | artifactId | Versão | Papel |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| raiz (`pom.xml`) | `br.com.seedgrid` | `seedgrid-resilience-core-parent` | `2026.10.13` | POM agregador; parent `seedgrid-parent:2026.07.02`. Módulos: `deployment`, `runtime` |
|
|
26
|
+
| `runtime` | `br.com.seedgrid` | `seedgrid-resilience-core` | `2026.10.13` | Anotações, interceptors, serviços, SPI, filtros JAX-RS. Gera `META-INF/jandex.idx` (jandex-maven-plugin) |
|
|
27
|
+
| `deployment` | `br.com.seedgrid` | `seedgrid-resilience-core-deployment` | `2026.10.13` | `SeedgridResilienceCoreProcessor`: só registra a feature `seedgrid-resilience-core` |
|
|
28
|
+
| `integration-tests` | `br.com.seedgrid` | `seedgrid-resilience-core-integration-tests` | — | **Não** listado em `<modules>` do POM raiz; contém apenas um recurso "hello" de template |
|
|
29
|
+
|
|
30
|
+
Observações:
|
|
31
|
+
- `quarkus-extension.yaml`: nome "SeedGrid Resilience Core", status `preview`.
|
|
32
|
+
|
|
33
|
+
## Dependências
|
|
34
|
+
|
|
35
|
+
| Dependência | Escopo | Uso |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `io.quarkus:quarkus-arc` | compile | CDI / interceptors |
|
|
38
|
+
| `br.com.seedgrid:seedgrid-cache-core:2026.10.13` | compile | `SeedGridCacheProvider` (`increment`, `getBytes`, `putBytes`, `withLock`) |
|
|
39
|
+
| `jakarta.ws.rs:jakarta.ws.rs-api` | provided | `RateLimitExceptionMapper`, `RateLimitContainerResponseFilter` |
|
|
40
|
+
| `io.quarkus:quarkus-vertx-http` | provided | `IpIdentityResolver` (injeção opcional de `HttpServerRequest`) |
|
|
41
|
+
| `seedgrid-cache-redis`, `quarkus-redis-client`, testcontainers, mockito | test | Testes |
|
|
42
|
+
|
|
43
|
+
Provider de cache: o `seedgrid-cache-core` traz `DefaultSeedGridCache` como `@DefaultBean` (em memória); os providers `seedgrid-cache-redis`/`-valkey`/`-dragonfly` são `@Alternative`. Sem um provider distribuído no classpath, o estado de rate limit/circuit breaker fica **por processo** (`DefaultSeedGridCache`, `@DefaultBean` em `seedgrid-cache-core`; o teste `SeedgridResilienceCoreTest` sobe sem provider nem config). **Confirmado**.
|
|
44
|
+
|
|
45
|
+
## Arquitetura e fluxo
|
|
46
|
+
|
|
47
|
+
```mermaid
|
|
48
|
+
flowchart LR
|
|
49
|
+
CALL["Chamada a método anotado"] --> RLI["RateLimitInterceptor<br/>prioridade LIBRARY_BEFORE"]
|
|
50
|
+
RLI --> RLS["RateLimitService<br/>(lê seedgrid.ratelimit.{configKey}.*)"]
|
|
51
|
+
RLS --> IDR["IdentityResolver"]
|
|
52
|
+
RLS --> ALG["Fixed / Sliding / Token bucket"]
|
|
53
|
+
ALG --> CACHE[("SeedGridCacheProvider")]
|
|
54
|
+
RLI -->|"negado"| EX429["RateLimitExceededException → 429"]
|
|
55
|
+
RLI --> CBI["CircuitBreakerInterceptor<br/>prioridade LIBRARY_BEFORE+10"]
|
|
56
|
+
CBI --> CBR["CircuitBreakerConfigResolver<br/>(lê seedgrid.circuitbreaker.{name}.*)"]
|
|
57
|
+
CBI --> STORE["CacheBackedCircuitBreakerStore"] --> CACHE
|
|
58
|
+
CBI --> M["método de negócio / fallbackMethod"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Rate limit (`@SeedGridRateLimit`)
|
|
62
|
+
1. `RateLimitInterceptor` resolve a anotação: método → método na classe alvo → hierarquia de classes → interfaces.
|
|
63
|
+
2. `RateLimitService#tryAcquire`: se `configKey` não vazio e `seedgrid.ratelimit.{configKey}.enabled=false`, permite sem contar. Senão resolve identidade, permits, janela, min-spacing, burst e algoritmo (config vence anotação quando há `configKey`), valida (`permits > 0`, `window > 0`, `burstCapacity >= 0`, `minSpacing >= 0`, senão `IllegalArgumentException`).
|
|
64
|
+
3. Chave no cache: `rl:{tenant}:{operation}:{scope}:{identity}` (sem `operation` quando vazio). `tenant` vem do atributo `tenantId` do `SeedGridInvocationContext` (padrão `no-tenant`); `operation` do atributo `operation`. `scope`: `METHOD` → assinatura do método; `CLASS` → nome da classe alvo; `GROUP` → `group` (ou assinatura se vazio).
|
|
65
|
+
4. Algoritmos:
|
|
66
|
+
- `FIXED_WINDOW`: `cache.increment(key, window)`. **Não aplica `minSpacing`** (o campo é ignorado nesse algoritmo — **Confirmado**).
|
|
67
|
+
- `SLIDING_WINDOW`: lista de timestamps sob `withLock`; aplica `minSpacing`.
|
|
68
|
+
- `TOKEN_BUCKET`: capacidade = `burstCapacity` (ou `permits` se 0), reposição `permits/window`; aplica `minSpacing`.
|
|
69
|
+
5. Negado → `RateLimitExceededException`; `RateLimitExceptionMapper` (JAX-RS) responde **429** com `X-RateLimit-Limit`, `X-RateLimit-Remaining: 0`, `X-RateLimit-Reset` e `Retry-After` (segundos, arredondados para cima).
|
|
70
|
+
6. Permitido e `emitHeaders=true` → `RateLimitHeaderCarrier` (ThreadLocal) e `RateLimitContainerResponseFilter` acrescentam `X-RateLimit-Limit/Remaining/Reset` à resposta.
|
|
71
|
+
|
|
72
|
+
### Circuit breaker (`@SeedGridCircuitBreaker`)
|
|
73
|
+
1. `CircuitBreakerInterceptor` resolve a anotação (método → método na classe alvo → hierarquia).
|
|
74
|
+
2. Kill switch: `seedgrid.circuitbreaker.{name}.enabled=false` → chama o método direto.
|
|
75
|
+
3. Nome do circuito = `name` ou, se vazio, assinatura `Classe#metodo(tipos)`. Chave no cache: `cb:{nome}`.
|
|
76
|
+
4. `CacheBackedCircuitBreakerStore` (sob `withLock` de 1 s): estados `CLOSED` → `OPEN` (taxa de falha ≥ `failureRateThreshold` ou, se `slowCallDurationThresholdMs > 0`, taxa de lentas ≥ `slowCallRateThreshold`, após `minimumNumberOfCalls`) → `HALF_OPEN` após `openStateDurationMs` → `CLOSED` quando `permittedCallsInHalfOpen` sondas têm sucesso, ou volta a `OPEN` na primeira falha.
|
|
77
|
+
5. Janela: ring buffer de tamanho `slidingWindowSize`. Em `TIME_BASED`, `slidingWindowSize` é **também** a duração em segundos da janela (e o tamanho máximo do buffer). Alterar `slidingWindowSize` descarta o estado salvo. TTL do estado: `max(openStateDurationMs*2, 1h)`.
|
|
78
|
+
6. Exceções: conta como falha se for subclasse de algum `recordExceptions` e não de `ignoreExceptions` (ignore vence).
|
|
79
|
+
7. Circuito aberto → `CircuitBreakerOpenException(circuitName, retryAfterMs)`; com `fallbackMethod`, chama o fallback. O fallback também é chamado em **qualquer** exceção do método (mesmo as ignoradas). Assinaturas procuradas, em ordem: `(params..., TipoDaExceção ou superclasse)`, `(params...)`, `()`; precisa ser método **público** (`Class#getMethod`).
|
|
80
|
+
|
|
81
|
+
## APIs públicas e pontos de integração
|
|
82
|
+
|
|
83
|
+
| Tipo | Símbolo | Descrição |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| Anotação (InterceptorBinding) | `...ratelimiter.annotations.SeedGridRateLimit` | `permits`, `window`, `windowUnit`, `algorithm`, `burstCapacity`, `minSpacing`, `minSpacingUnit`, `identity`, `group`, `scope`, `emitHeaders`, `configKey` (detalhes em [[SeedGrid/Extensions/resilience/Configuracao\|Configuração]]) |
|
|
86
|
+
| Anotação (InterceptorBinding) | `...circuitbreaker.annotations.SeedGridCircuitBreaker` | `name`, `slidingWindowSize`, `windowType`, `failureRateThreshold`, `slowCallDurationThresholdMs`, `slowCallRateThreshold`, `minimumNumberOfCalls`, `openStateDurationMs`, `permittedCallsInHalfOpen`, `recordExceptions`, `ignoreExceptions`, `fallbackMethod` |
|
|
87
|
+
| SPI | `...ratelimiter.spi.IdentityResolver` | `String resolve(SeedGridInvocationContext ctx)`; prontos: `IpIdentityResolver` (X-Forwarded-For → X-Real-IP → remote address → `anonymous`), `AnonymousIdentityResolver` |
|
|
88
|
+
| SPI | `...circuitbreaker.spi.CircuitBreakerStore` | `allowCall`, `onSuccess`, `onError`; implementação `CacheBackedCircuitBreakerStore` (`@ApplicationScoped`, sem `@DefaultBean`) |
|
|
89
|
+
| SPI | `...ratelimiter.algorithm.RateLimitAlgorithmStrategy` | `acquire(SeedGridCacheProvider, RateLimitRequest)` |
|
|
90
|
+
| Contexto | `...core.SeedGridInvocationContext` / `ThreadLocalSeedGridInvocationContext` | `set/clear/runWith/callWith`; atributos `tenantId` e `operation` entram na chave do rate limit. A extension **não** popula esse contexto: o host precisa fazê-lo (senão usa `EmptySeedGridInvocationContext`) |
|
|
91
|
+
| Beans CDI | `RateLimitService`, `RateLimitService.Clock`, `CircuitBreakerConfigResolver`, algoritmos | `@ApplicationScoped` |
|
|
92
|
+
| Exceções | `RateLimitExceededException` (mapeada para 429), `CircuitBreakerOpenException` | `CircuitBreakerOpenException` **não** tem `ExceptionMapper` na extension — **Inferência**: em endpoint REST sem fallback vira erro 500 se o host não mapear |
|
|
93
|
+
| JAX-RS | `RateLimitExceptionMapper`, `RateLimitContainerResponseFilter` (`@Provider`) | Headers `X-RateLimit-*`, `Retry-After` |
|
|
94
|
+
|
|
95
|
+
Endpoints REST próprios: nenhum no runtime (o `integration-tests` tem só `GET /seedgrid-resilience-core` de template). Migrations: nenhuma. Eventos: nenhum.
|
|
96
|
+
|
|
97
|
+
## Testes existentes (não executados)
|
|
98
|
+
|
|
99
|
+
| Local | Tipo | Cobre |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `runtime/src/test/.../ratelimiter/RateLimitAlgorithmsIT.java` | IT com Redis (Testcontainers, `disabledWithoutDocker`) | Os 3 algoritmos: limite, reset, min-spacing, burst, refill |
|
|
102
|
+
| `runtime/src/test/.../circuitbreaker/CircuitBreakerStoreIT.java` | IT com Redis | Transições de estado, slow calls, record/ignore, janela por tempo, persistência |
|
|
103
|
+
| `runtime/src/test/.../ratelimiter/services/RateLimitServiceConfigOverrideTest.java` | Unitário (Mockito `Config`) | Overrides `seedgrid.ratelimit.checkout.*`, kebab-case em `algorithm`, sem `configKey` ignora config |
|
|
104
|
+
| `runtime/src/test/.../circuitbreaker/configs/CircuitBreakerConfigResolverTest.java` | Unitário | Overrides `seedgrid.circuitbreaker.payments.*`, sem `name` ignora config, `enabled` |
|
|
105
|
+
| `runtime/src/test/.../ratelimiter/spi/IpIdentityResolverTest.java`, `AnonymousIdentityResolverTest.java` | Unitário | Resolução de identidade |
|
|
106
|
+
| `deployment/src/test/.../SeedgridResilienceCoreTest.java`, `SeedgridResilienceCoreDevModeTest.java` | QuarkusUnitTest / DevModeTest | Boot sem camada HTTP e sem config |
|
|
107
|
+
|
|
108
|
+
## Limitações
|
|
109
|
+
- `minSpacing` não tem efeito com `FIXED_WINDOW`.
|
|
110
|
+
- O kill switch do rate limiter (`enabled=false`) ainda emite headers `X-RateLimit-*` (limite = `permits` da anotação), quando `emitHeaders=true`.
|
|
111
|
+
- Valores inválidos de `algorithm`/`window-type` só falham na chamada (`IllegalArgumentException` de `Enum.valueOf`), não no boot.
|
|
112
|
+
- `CircuitBreakerOpenException` sem mapper HTTP.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, extension, runtime, rust, configuracao]
|
|
3
|
+
fonte: rust/seedgrid-runtime, rust/seedgrid-macros
|
|
4
|
+
branch: main
|
|
5
|
+
commit: be738a2d1e
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# runtime — Configuração Rust
|
|
10
|
+
|
|
11
|
+
Visão geral: [[SeedGrid/Extensions/runtime/Visao-geral|Visão geral do runtime]]. Não há equivalente Java (o análogo é o comportamento do container Quarkus).
|
|
12
|
+
|
|
13
|
+
## Cabeçalho
|
|
14
|
+
| Item | Valor |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Mecanismos | (1) `BootstrapOptions` em runtime; (2) **atributos das macros** (fixados em compilação); (3) uma leitura de ambiente: `default_gate(key)` |
|
|
17
|
+
| Variáveis de ambiente | **nenhum nome fixo**: `default_gate` lê a variável cujo nome é o valor de `#[scheduled(enabled_if = "...")]` (`rust/seedgrid-runtime/src/bootstrap.rs` → `default_gate`) |
|
|
18
|
+
| Features Cargo | nenhuma (nas duas crates) |
|
|
19
|
+
|
|
20
|
+
## Runtime: `BootstrapOptions` e gate
|
|
21
|
+
| Propriedade | Tipo | Valores aceitos | Padrão | Obrigatória | Comportamento | Uso | Relações | Leitura | Origem |
|
|
22
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
23
|
+
| `BootstrapOptions.fail_fast` | bool | — | `true` | Não | primeiro erro de `#[startup]` aborta o boot (`Err` com contexto); `false` loga e segue | `run_startups` | — | `bootstrap_with` | `src/bootstrap.rs` |
|
|
24
|
+
| `BootstrapOptions.run_startup` | bool | — | `true` | Não | roda as `#[startup]` | `bootstrap_with` | — | idem | idem |
|
|
25
|
+
| `BootstrapOptions.run_scheduled` | bool | — | `true` | Não | spawna as `#[scheduled]` | `bootstrap_with` | — | idem | idem |
|
|
26
|
+
| `BootstrapOptions.gate` (`with_gate`) | `Fn(&str) -> bool` | — | `default_gate` | Não | decide se cada `enabled_if` está ligado | `spawn_all_scheduled` | `enabled_if` | idem | idem |
|
|
27
|
+
| `<valor de enabled_if>` (env var dinâmica) | bool | `true`, `1`, `yes`, `on` (case-insensitive, `trim`); ausente/outro = desligado | desligado | Condicional: só para tasks com `enabled_if` | task não é spawnada quando falso (log `info`) | `default_gate` | substituível por `with_gate` | no `bootstrap` | `src/bootstrap.rs` → `default_gate` |
|
|
28
|
+
|
|
29
|
+
No workspace, nenhuma crate de plataforma declara `enabled_if`; só aparecem `FEATURE_REPORTS` (teste `tests/macros.rs`) e `REPORTS_ENABLED` (doc) como exemplos.
|
|
30
|
+
|
|
31
|
+
## Macros (configuração em compilação)
|
|
32
|
+
|
|
33
|
+
### `#[startup]`
|
|
34
|
+
| Atributo | Tipo | Padrão | Observação |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `priority` | i32 | `100` (neutra; `seedgrid-macros/src/lib.rs` → `startup`) | menor roda antes |
|
|
37
|
+
|
|
38
|
+
### `#[scheduled]` (exatamente um de `cron`/`every`)
|
|
39
|
+
| Atributo | Tipo | Padrão | Observação |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| `cron` | String | — | quartz de 6 campos (`seg min hora dia-mes mes dia-semana`); inválido → task não iniciada (log de erro) |
|
|
42
|
+
| `every` | String | — | `Nms`, `Ns`, `Nm`, `Nh`, `Nd`; zero/inválido → não iniciada |
|
|
43
|
+
| `enabled_if` | String | — (sempre ligada) | nome da env var do gate |
|
|
44
|
+
| `concurrent` | bool | `false` (SKIP) | `true` = PROCEED |
|
|
45
|
+
| `delay` | String | — | atraso inicial, mesmo formato de `every` |
|
|
46
|
+
|
|
47
|
+
### `#[default_provider]`
|
|
48
|
+
`spi` (obrigatório), `provider`, `priority` (padrão `0`; maior vence em `resolve_spi`).
|
|
49
|
+
|
|
50
|
+
### `#[rate_limit]` (→ `seedgrid-resilience`)
|
|
51
|
+
| Atributo | Padrão | Observação |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `config_key` | `""` | habilita overrides `seedgrid.ratelimit.<key>.*` da `ConfigSource` do `RateLimitService` ([[SeedGrid/Extensions/resilience/Configuracao-Rust\|Configuração Rust do resilience]]) |
|
|
54
|
+
| `permits` | `5` | |
|
|
55
|
+
| `window` | `"30s"` | |
|
|
56
|
+
| `algorithm` | `"fixed_window"` | `fixed_window`, `sliding_window`, `token_bucket` |
|
|
57
|
+
| `service` | — | **obrigatório**: caminho de função que devolve `Arc<RateLimitService>` |
|
|
58
|
+
| `context` | `EmptySeedGridInvocationContext` | expressão de contexto (limite global por método se omitido) |
|
|
59
|
+
|
|
60
|
+
Retorno exigido: `Result<T, E>` com `E: From<RateLimitExceededException>`.
|
|
61
|
+
|
|
62
|
+
### `#[circuit_breaker]` (→ `seedgrid-resilience`)
|
|
63
|
+
| Atributo | Padrão | Observação |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `name` | — | **obrigatório** |
|
|
66
|
+
| `failure_rate_threshold` | `50.0` | |
|
|
67
|
+
| `open_state_duration` | `"30s"` | |
|
|
68
|
+
| `minimum_number_of_calls` | `20` | |
|
|
69
|
+
| `sliding_window_size` | `100` | |
|
|
70
|
+
| `cache` | — | **obrigatório**: provider de `Arc<InMemoryCache>` ou `Arc<dyn SeedGridCacheProvider>` |
|
|
71
|
+
| `fallback` | — | função sem argumentos com o mesmo `Result<T, E>` |
|
|
72
|
+
|
|
73
|
+
### `#[audited]` (→ `seedgrid-audit`)
|
|
74
|
+
`entity` (FQCN, obrigatório), `operation` (`CREATE`, `UPDATE`, `DELETE`, `READ`, `OTHER`; obrigatório), `description`, `auditor` (obrigatório; provider de `impl Auditor`), `user` (expr), `context` (expr `AuditContext`, vence `user`), `before` (expr avaliada antes do corpo), `mask_rules` (expr `Vec<MaskRule>`), `capture_return` (padrão `true` em CREATE/UPDATE; serializa `Ok(T)`, exige `T: Serialize`). Audita só no sucesso.
|
|
75
|
+
|
|
76
|
+
### `#[derive(PersonalData)]` (→ `seedgrid-privacy`)
|
|
77
|
+
Atributo de campo `#[personal_data(...)]` (alias `#[encrypted(...)]`): `search = "campo_hash"`, `normalize = "email" | "canonical" | "none"` (padrão `canonical`), `nested` (desce em struct que também deriva `PersonalData`). Ver [[SeedGrid/Extensions/privacy/Configuracao-Rust|Configuração Rust do privacy]].
|
|
78
|
+
|
|
79
|
+
Fonte de todos os atributos: `rust/seedgrid-macros/src/lib.rs` (parsers `meta.path.is_ident(...)`) e README do runtime. Padrões de `#[startup]` e `#[rate_limit]` conferidos nos parsers; os de `#[circuit_breaker]` são `Option` no parser e o valor efetivo vem dos defaults de `SeedGridCircuitBreaker` (README; **Inferência** coerente com `config_resolver.rs`); os de `#[audited]` vêm do README.
|
|
80
|
+
|
|
81
|
+
## Somente exemplos/testes
|
|
82
|
+
| Variável | Onde |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `FEATURE_REPORTS` | `tests/macros.rs` (gate de teste) |
|
|
85
|
+
| `RUST_LOG` | não usado; os exemplos fixam o nível em código |
|
|
86
|
+
|
|
87
|
+
## Receitas
|
|
88
|
+
- **Ligar uma rotina só em produção**: `#[scheduled(cron = "0 0 3 * * ?", enabled_if = "NIGHTLY_JOB_ENABLED")]` + `NIGHTLY_JOB_ENABLED=true` no ambiente.
|
|
89
|
+
- **Gate a partir de outra fonte (ex.: arquivo de config)**: `bootstrap_with(BootstrapOptions::new().with_gate(|k| minha_config.flag(k)))`.
|
|
90
|
+
- **Boot tolerante a falhas**: `BootstrapOptions { fail_fast: false, ..Default::default() }`.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, extension, runtime, rust]
|
|
3
|
+
fonte: rust/seedgrid-runtime, rust/seedgrid-macros
|
|
4
|
+
branch: main
|
|
5
|
+
commit: be738a2d1e
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# runtime — Visão geral (só Rust: `seedgrid-runtime` + `seedgrid-macros`)
|
|
10
|
+
|
|
11
|
+
Par de crates que reproduz em Rust, **em tempo de compilação**, o auto-wiring do Quarkus/CDI que os hosts SeedGrid têm no Java: anotar funções e chamar um único `bootstrap()`. Não há extensão Java equivalente (é infraestrutura do lado Rust). Configuração e referência dos atributos das macros: [[SeedGrid/Extensions/runtime/Configuracao-Rust|Configuração Rust do runtime]].
|
|
12
|
+
|
|
13
|
+
| Crate | Papel |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `seedgrid-runtime` (lib `seedgrid_runtime`) | registro `inventory`, `bootstrap()`/`RuntimeHandles`, motor de agendamento (cron/intervalo), eventos ao usuário (`events`); reexporta as macros, `inventory` e `anyhow` |
|
|
16
|
+
| `seedgrid-macros` (lib `seedgrid_macros`, `proc-macro = true`) | attribute macros `#[startup]`, `#[scheduled]`, `#[default_provider]`, `#[rate_limit]`, `#[circuit_breaker]`, `#[audited]` e o derive `#[derive(PersonalData)]` |
|
|
17
|
+
|
|
18
|
+
Ambas: versão do workspace `2026.10.14`, edição 2021, MSRV 1.82, sem features Cargo. `seedgrid-runtime` depende de `seedgrid-macros`, `inventory 0.3`, `tokio`, `chrono`; resilience, cache e audit são só **dev-dependencies** (exemplo/teste). Recomendação do README: depender só de `seedgrid-runtime` (o código gerado usa `::seedgrid_runtime::...`); a exceção é `#[derive(PersonalData)]`, que gera `::seedgrid_privacy::...` e é reexportado pela crate privacy.
|
|
19
|
+
|
|
20
|
+
## Mapeamento CDI/Quarkus → Rust
|
|
21
|
+
| Java | Rust | Fase |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `@Observes StartupEvent` / `@Startup` + `@Priority` | `#[startup(priority = N)]` (menor roda antes) | 1 |
|
|
24
|
+
| `@Scheduled(cron / every)` | `#[scheduled(cron = "...")]` / `#[scheduled(every = "...")]` | 1 |
|
|
25
|
+
| gate `{config.key}` do `@Scheduled` | `#[scheduled(..., enabled_if = "ENV_VAR")]` | 1 |
|
|
26
|
+
| `ConcurrentExecution.SKIP` / `PROCEED` | `concurrent = false` (padrão) / `true` | 1 |
|
|
27
|
+
| `@DefaultBean` | `#[default_provider(spi, provider, priority)]` + `resolve_spi` (só metadados; maior prioridade vence) | 1 |
|
|
28
|
+
| `@SeedGridRateLimit` | `#[rate_limit(...)]` → `seedgrid-resilience` | 2 |
|
|
29
|
+
| `@SeedGridCircuitBreaker` | `#[circuit_breaker(...)]` → `seedgrid-resilience` | 2 |
|
|
30
|
+
| `@Audit` | `#[audited(...)]` → `seedgrid-audit` | 3 |
|
|
31
|
+
| `@PersonalData` / `@EncryptedField` | `#[derive(PersonalData)]` → `seedgrid-privacy` | — |
|
|
32
|
+
| `Event<SgUserEvent>` + `@Observes(AFTER_SUCCESS)` | `events::UserEventSink` / `SgUserEvent` | — |
|
|
33
|
+
| boot / shutdown do container | `bootstrap()`, `bootstrap_with(opts)` / `RuntimeHandles::shutdown()` | 1 |
|
|
34
|
+
|
|
35
|
+
Fonte: `rust/seedgrid-runtime/src/lib.rs`.
|
|
36
|
+
|
|
37
|
+
## Arquitetura
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart LR
|
|
40
|
+
A["#[startup] / #[scheduled] / #[default_provider]"] -- inventory::submit! --> R[(slices globais em link-time)]
|
|
41
|
+
R --> B["bootstrap()"]
|
|
42
|
+
B -->|ordem de priority| S["roda #[startup] (sequencial)"]
|
|
43
|
+
B -->|enabled_if via gate| T["spawn_scheduled_boxed (tokio)"]
|
|
44
|
+
T --> H[RuntimeHandles]
|
|
45
|
+
L["libs: spawn_scheduled(SpawnConfig, factory)"] --> T
|
|
46
|
+
```
|
|
47
|
+
- Fase 1 usa `inventory`: o linker precisa manter os símbolos (funciona em binários, exemplos e testes de integração; numa lib nunca referenciada pelo binário, itens podem ser descartados — caveat do README).
|
|
48
|
+
- Fases 2/3 **não** usam `inventory`: são interceptors por função que embrulham o corpo e obtêm dependências via funções-provider sem argumentos (`service`, `cache`, `auditor`).
|
|
49
|
+
- `startup` com erro: `fail_fast = true` (padrão) aborta o boot; senão loga e continua. Erros de `#[scheduled]` só são logados (a task sobrevive).
|
|
50
|
+
|
|
51
|
+
## API pública (`seedgrid-runtime`)
|
|
52
|
+
| Grupo | Símbolos | Fonte |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Boot | `bootstrap()`, `bootstrap_with(BootstrapOptions)`, `BootstrapOptions { fail_fast, run_startup, run_scheduled, gate }`, `BootstrapOptions::with_gate`, `default_gate`, `EnabledGate` | `src/bootstrap.rs` |
|
|
55
|
+
| Handles | `RuntimeHandles::{scheduled_task_count, task_names, abort_all, shutdown}` | idem |
|
|
56
|
+
| Agendamento programático (para libs) | `spawn_scheduled(SpawnConfig, factory) -> JoinHandle`, `SpawnConfig::{new, concurrent, with_initial_delay}`, `RunSchedule::{cron, every, from_spec}`, `JobFactory` | `src/scheduler.rs` |
|
|
57
|
+
| Cron / duração | `CronSchedule::{parse, matches, next_after}` (quartz de 6 campos: `*`, `?`, `*/n`, `a-b`, `a,b`; dia da semana 0=domingo, aceita 7), `parse_duration` (`ms`, `s`, `m`, `h`, `d`; zero → `None`) | `src/cron.rs` |
|
|
58
|
+
| Registro | `StartupItem`, `ScheduledItem`, `ScheduleSpec`, `SpiBinding`, `TaskRunner`, `BoxFuture`, `resolve_spi`, `startup_items_sorted`, `scheduled_items` | `src/registry.rs` |
|
|
59
|
+
| Eventos | `events::{SgUserEvent, UserEventSink, NoopUserEventSink, noop_user_event_sink}` | `src/events.rs` |
|
|
60
|
+
|
|
61
|
+
## Quem usa no workspace (**Confirmado** por grep)
|
|
62
|
+
| Crate | O que usa |
|
|
63
|
+
|---|---|
|
|
64
|
+
| backup | `events::{UserEventSink, SgUserEvent, noop_user_event_sink}` (aviso de export concluído) |
|
|
65
|
+
| realtime | `events::*` (implementa `UserEventSink`), `parse_duration` |
|
|
66
|
+
| subdomain-tenancy | `spawn_scheduled`, `RunSchedule`, `SpawnConfig`, `CronSchedule`, `events` |
|
|
67
|
+
| privacy | `#[derive(PersonalData)]` (via `seedgrid-macros`) |
|
|
68
|
+
| — | Nenhuma crate de plataforma usa `#[startup]`, `#[scheduled]`, `#[rate_limit]`, `#[circuit_breaker]` ou `#[audited]` fora dos exemplos/testes do runtime |
|
|
69
|
+
|
|
70
|
+
## Exemplo (de `rust/seedgrid-runtime/examples/quickstart.rs`)
|
|
71
|
+
```rust
|
|
72
|
+
use anyhow::Result;
|
|
73
|
+
use seedgrid_runtime::{scheduled, startup};
|
|
74
|
+
|
|
75
|
+
#[startup(priority = 10)]
|
|
76
|
+
async fn seed_database() -> Result<()> { Ok(()) }
|
|
77
|
+
|
|
78
|
+
#[scheduled(every = "2s")]
|
|
79
|
+
async fn heartbeat() -> Result<()> { Ok(()) }
|
|
80
|
+
|
|
81
|
+
#[scheduled(cron = "0 0 * * * *")]
|
|
82
|
+
async fn hourly_report() -> Result<()> { Ok(()) }
|
|
83
|
+
|
|
84
|
+
#[tokio::main]
|
|
85
|
+
async fn main() -> Result<()> {
|
|
86
|
+
let handles = seedgrid_runtime::bootstrap().await?;
|
|
87
|
+
tokio::time::sleep(std::time::Duration::from_millis(5100)).await;
|
|
88
|
+
handles.shutdown().await;
|
|
89
|
+
Ok(())
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
Fases 2/3 completas: `rust/seedgrid-runtime/examples/resilience_audit.rs` (`cargo run -p seedgrid-runtime --example resilience_audit`). API programática (doc de `src/scheduler.rs`):
|
|
93
|
+
```rust
|
|
94
|
+
let schedule = RunSchedule::cron("0 0 * * * ?").expect("cron valido");
|
|
95
|
+
let handle = spawn_scheduled(SpawnConfig::new("nightly_cleanup", schedule), || async { Ok(()) });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Testes
|
|
99
|
+
`rust/seedgrid-runtime/tests/macros.rs` (coleta e ordem de `#[startup]`, gate `enabled_if` ligado/desligado, task disparando, `default_provider`) e `tests/resilience_audit.rs` (macros de Fase 2/3); unitários em `src/cron.rs`. **Não executados.**
|
|
100
|
+
|
|
101
|
+
## Limitações
|
|
102
|
+
- `#[startup]`/`#[scheduled]` não recebem argumentos; a função deve ser `async fn() -> anyhow::Result<()>` ou `-> ()`.
|
|
103
|
+
- Empate de `priority` tem ordem indefinida.
|
|
104
|
+
- `#[default_provider]` registra só metadados (sem DI real) — roadmap do README.
|
|
105
|
+
|
|
106
|
+
## Enums
|
|
107
|
+
Sem equivalente Java. Públicos:
|
|
108
|
+
|
|
109
|
+
| Enum | Arquivo | Variantes / uso |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| `ScheduleSpec` | `seedgrid-runtime/src/registry.rs` | `Cron(&'static str)` ou `Every(&'static str)` — gerado por `#[scheduled(cron=…)]` / `#[scheduled(every=…)]` (exatamente um dos dois) |
|
|
112
|
+
| `RunSchedule` | `seedgrid-runtime/src/scheduler.rs` | `Interval(Duration)` ou `Cron(CronSchedule)` — schedule já compilado (`RunSchedule::cron`, `every`, `from_spec`); `None` se a expressão for inválida |
|
|
113
|
+
|
|
114
|
+
As macros (`seedgrid-macros`) não definem enums; o derive `PersonalData` usa `SearchNormalization` ([[SeedGrid/Extensions/privacy/Rust#Enums|Enums do privacy Rust]]).
|