sirius-framework-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +160 -0
  3. package/dist/grammars/tree-sitter-java.wasm +0 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +247 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/java-parser.d.ts +25 -0
  8. package/dist/java-parser.js +281 -0
  9. package/dist/java-parser.js.map +1 -0
  10. package/dist/prompts/index.d.ts +9 -0
  11. package/dist/prompts/index.js +15 -0
  12. package/dist/prompts/index.js.map +1 -0
  13. package/dist/prompts/workflows.d.ts +27 -0
  14. package/dist/prompts/workflows.js +317 -0
  15. package/dist/prompts/workflows.js.map +1 -0
  16. package/dist/resources/biz/analytics.md +157 -0
  17. package/dist/resources/biz/biz-controller.md +151 -0
  18. package/dist/resources/biz/codelists.md +154 -0
  19. package/dist/resources/biz/entity-triple.md +142 -0
  20. package/dist/resources/biz/importer.md +153 -0
  21. package/dist/resources/biz/isenguard.md +156 -0
  22. package/dist/resources/biz/jobs.md +145 -0
  23. package/dist/resources/biz/processes.md +155 -0
  24. package/dist/resources/biz/storage.md +149 -0
  25. package/dist/resources/biz/tenants.md +159 -0
  26. package/dist/resources/biz/testing.md +127 -0
  27. package/dist/resources/db/composites.md +145 -0
  28. package/dist/resources/db/entities.md +156 -0
  29. package/dist/resources/db/mixing.md +176 -0
  30. package/dist/resources/db/queries.md +178 -0
  31. package/dist/resources/db/refs.md +135 -0
  32. package/dist/resources/index.d.ts +27 -0
  33. package/dist/resources/index.js +68 -0
  34. package/dist/resources/index.js.map +1 -0
  35. package/dist/resources/kernel/async.md +189 -0
  36. package/dist/resources/kernel/commons.md +203 -0
  37. package/dist/resources/kernel/config.md +155 -0
  38. package/dist/resources/kernel/di.md +138 -0
  39. package/dist/resources/kernel/lifecycle.md +146 -0
  40. package/dist/resources/loader.d.ts +9 -0
  41. package/dist/resources/loader.js +17 -0
  42. package/dist/resources/loader.js.map +1 -0
  43. package/dist/resources/web/controllers.md +151 -0
  44. package/dist/resources/web/services.md +136 -0
  45. package/dist/resources/web/templates.md +162 -0
  46. package/dist/tools/index.d.ts +4 -0
  47. package/dist/tools/index.js +3 -0
  48. package/dist/tools/index.js.map +1 -0
  49. package/dist/tools/introspection.d.ts +55 -0
  50. package/dist/tools/introspection.js +233 -0
  51. package/dist/tools/introspection.js.map +1 -0
  52. package/dist/tools/scaffold.d.ts +64 -0
  53. package/dist/tools/scaffold.js +505 -0
  54. package/dist/tools/scaffold.js.map +1 -0
  55. package/dist/workspace.d.ts +37 -0
  56. package/dist/workspace.js +185 -0
  57. package/dist/workspace.js.map +1 -0
  58. package/package.json +41 -0
@@ -0,0 +1,178 @@
1
+ # Queries
2
+
3
+ Sirius-db provides a consistent query builder pattern across all three database
4
+ backends. Each mapper has its own `select()` method that returns a typed query
5
+ object, but the API surface is deliberately similar.
6
+
7
+ ## OMA — JDBC Queries (SmartQuery)
8
+
9
+ `OMA` is the mapper for `SQLEntity`. Inject it with `@Part`:
10
+
11
+ ```java
12
+ @Part
13
+ private OMA oma;
14
+ ```
15
+
16
+ **Select (query builder):**
17
+ ```java
18
+ SmartQuery<Product> query = oma.select(Product.class)
19
+ .eq(Product.CATEGORY, "electronics")
20
+ .orderAsc(Product.NAME)
21
+ .limit(50);
22
+
23
+ List<Product> products = query.queryList();
24
+ ```
25
+
26
+ **Find by ID:**
27
+ ```java
28
+ Optional<Product> product = oma.find(Product.class, productId);
29
+ ```
30
+
31
+ **Count:**
32
+ ```java
33
+ long count = oma.select(Product.class)
34
+ .eq(Product.ACTIVE, true)
35
+ .count();
36
+ ```
37
+
38
+ **Exists check:**
39
+ ```java
40
+ boolean exists = oma.select(Product.class)
41
+ .eq(Product.SKU, sku)
42
+ .exists();
43
+ ```
44
+
45
+ **Iteration (streaming):**
46
+ ```java
47
+ oma.select(Product.class)
48
+ .eq(Product.ACTIVE, true)
49
+ .iterateAll(product -> {
50
+ // process each product
51
+ });
52
+ ```
53
+
54
+ **Update and delete:**
55
+ ```java
56
+ oma.update(product);
57
+ oma.delete(product);
58
+ oma.forceDelete(product); // bypasses REJECT constraints
59
+ ```
60
+
61
+ ## Mango — MongoDB Queries (MongoQuery)
62
+
63
+ `Mango` is the mapper for `MongoEntity`:
64
+
65
+ ```java
66
+ @Part
67
+ private Mango mango;
68
+ ```
69
+
70
+ **Select:**
71
+ ```java
72
+ MongoQuery<MongoProduct> query = mango.select(MongoProduct.class)
73
+ .eq(MongoProduct.CATEGORY, "electronics")
74
+ .orderAsc(MongoProduct.NAME)
75
+ .limit(50);
76
+
77
+ List<MongoProduct> products = query.queryList();
78
+ ```
79
+
80
+ **Find by ID:**
81
+ ```java
82
+ Optional<MongoProduct> product = mango.find(MongoProduct.class, productId);
83
+ ```
84
+
85
+ The query API mirrors OMA: `count()`, `exists()`, `iterateAll()`, `queryList()`,
86
+ `queryFirst()`, `delete()`, and `update()` all work the same way.
87
+
88
+ ## Elastic — Elasticsearch Queries (ElasticQuery)
89
+
90
+ `Elastic` is the mapper for `ElasticEntity`:
91
+
92
+ ```java
93
+ @Part
94
+ private Elastic elastic;
95
+ ```
96
+
97
+ **Select:**
98
+ ```java
99
+ ElasticQuery<EventLog> query = elastic.select(EventLog.class)
100
+ .eq(EventLog.EVENT_TYPE, "login")
101
+ .orderDesc(EventLog.TIMESTAMP)
102
+ .limit(100);
103
+
104
+ List<EventLog> events = query.queryList();
105
+ ```
106
+
107
+ **Find by ID:**
108
+ ```java
109
+ Optional<EventLog> event = elastic.find(EventLog.class, eventId);
110
+ ```
111
+
112
+ Elastic queries also support full-text search and aggregations, but the basic
113
+ filter/sort/limit API is the same as OMA and Mango.
114
+
115
+ ## Common Query Methods
116
+
117
+ All three query types share these methods from the `Query` base class:
118
+
119
+ | Method | Description |
120
+ |--------|-------------|
121
+ | `eq(Mapping, value)` | Equals filter |
122
+ | `ne(Mapping, value)` | Not-equals filter |
123
+ | `gt(Mapping, value)` | Greater than |
124
+ | `gte(Mapping, value)` | Greater than or equal |
125
+ | `lt(Mapping, value)` | Less than |
126
+ | `lte(Mapping, value)` | Less than or equal |
127
+ | `orderAsc(Mapping)` | Sort ascending |
128
+ | `orderDesc(Mapping)` | Sort descending |
129
+ | `limit(int)` | Maximum results |
130
+ | `skip(int)` | Skip n results |
131
+ | `queryList()` | Return all matching as list |
132
+ | `queryFirst()` | Return first match as Optional |
133
+ | `count()` | Return count of matches |
134
+ | `exists()` | Return true if any match exists |
135
+ | `iterateAll(Consumer)` | Stream all matches through a consumer |
136
+ | `delete()` | Delete all matching entities |
137
+
138
+ ## Filter Factories
139
+
140
+ Each mapper provides a `FILTERS` factory for complex constraints:
141
+
142
+ ```java
143
+ oma.select(Product.class)
144
+ .where(OMA.FILTERS.or(
145
+ OMA.FILTERS.eq(Product.CATEGORY, "electronics"),
146
+ OMA.FILTERS.eq(Product.CATEGORY, "appliances")
147
+ ))
148
+ .queryList();
149
+ ```
150
+
151
+ Mango and Elastic have equivalent `FILTERS` factories with the same API.
152
+
153
+ ## Querying Composite Fields
154
+
155
+ Use `Mapping.inner()` to query fields inside composites:
156
+
157
+ ```java
158
+ oma.select(Customer.class)
159
+ .eq(Customer.PERSON.inner(PersonData.LASTNAME), "Smith")
160
+ .queryList();
161
+ ```
162
+
163
+ ## Common Mistakes
164
+
165
+ 1. **Calling queryList() on unbounded queries** — Always set a `limit()` or use
166
+ `iterateAll()` for large result sets. An unbounded `queryList()` loads
167
+ everything into memory.
168
+
169
+ 2. **Using raw strings instead of Mapping constants** — Always use `Mapping.named()`
170
+ constants. Raw strings bypass compile-time safety and break on refactoring.
171
+
172
+ 3. **Mixing mapper types** — Use `oma` for `SQLEntity`, `mango` for `MongoEntity`,
173
+ and `elastic` for `ElasticEntity`. Each entity class is bound to exactly one
174
+ mapper.
175
+
176
+ 4. **Ignoring iterateAll cancellation** — `iterateAll()` respects `TaskContext`
177
+ cancellation. Long-running iterations should check `TaskContext.get().isActive()`
178
+ or will be automatically stopped when the task is cancelled.
@@ -0,0 +1,135 @@
1
+ # Entity References
2
+
3
+ Entity references model relationships between entities in sirius-db. Instead of
4
+ storing a raw foreign key, references are wrapped in typed ref objects that provide
5
+ lazy loading, delete behavior, and type safety.
6
+
7
+ ## BaseEntityRef<I, E> — Common Interface
8
+
9
+ All reference types extend `BaseEntityRef<I, E>` (in `sirius.db.mixing.types`),
10
+ where `I` is the ID type and `E` is the entity type. It stores the referenced
11
+ entity's ID and lazily loads the full entity on demand.
12
+
13
+ Key methods on any ref:
14
+ - `getId()` — returns the stored ID (without loading the entity)
15
+ - `getValue()` — loads and returns the referenced entity (cached after first load)
16
+ - `getValueIfPresent()` — returns the entity only if already loaded
17
+ - `setId(I)` — sets the reference by ID
18
+ - `setValue(E)` — sets the reference by entity instance
19
+ - `isEmpty()` / `isFilled()` — checks whether the reference is set
20
+ - `is(E)` — checks if the ref points to the given entity
21
+ - `hasWriteOnceSemantics()` — whether the ref is immutable after first save
22
+
23
+ ## OnDelete Behaviors
24
+
25
+ The `OnDelete` enum defines what happens when the referenced entity is deleted:
26
+
27
+ - `CASCADE` — the entity holding the reference is also deleted
28
+ - `SET_NULL` — the reference field is set to null
29
+ - `REJECT` — the delete is rejected (throws an exception)
30
+ - `IGNORE` — no action is taken; the reference may become dangling
31
+
32
+ ## SQLEntityRef<E>
33
+
34
+ For SQL entities, use `SQLEntityRef<E>`. The ID type is `Long`.
35
+
36
+ ```java
37
+ public class OrderItem extends SQLTenantAware {
38
+
39
+ public static final Mapping ORDER = Mapping.named("order");
40
+ private final SQLEntityRef<Order> order =
41
+ SQLEntityRef.on(Order.class, BaseEntityRef.OnDelete.CASCADE);
42
+
43
+ public static final Mapping PRODUCT = Mapping.named("product");
44
+ private final SQLEntityRef<Product> product =
45
+ SQLEntityRef.on(Product.class, BaseEntityRef.OnDelete.REJECT);
46
+
47
+ // getters...
48
+ public SQLEntityRef<Order> getOrder() { return order; }
49
+ public SQLEntityRef<Product> getProduct() { return product; }
50
+ }
51
+ ```
52
+
53
+ ### on() vs writeOnceOn()
54
+
55
+ - `SQLEntityRef.on(Class, OnDelete)` — creates a **mutable** reference. The value
56
+ can be changed at any time.
57
+ - `SQLEntityRef.writeOnceOn(Class, OnDelete)` — creates an **immutable** reference.
58
+ The value can only be set when the entity is new. Any attempt to change it after
59
+ the first save throws an exception.
60
+
61
+ Use `writeOnceOn` for structural relationships that must never change, like
62
+ "which tenant owns this record":
63
+
64
+ ```java
65
+ private final SQLEntityRef<SQLTenant> tenant =
66
+ SQLEntityRef.writeOnceOn(SQLTenant.class, BaseEntityRef.OnDelete.CASCADE);
67
+ ```
68
+
69
+ ### Weak References
70
+
71
+ SQL refs can be made "weak" to skip foreign key constraint creation in the database.
72
+ This improves write performance but means the database will not enforce referential
73
+ integrity:
74
+
75
+ ```java
76
+ private final SQLEntityRef<AuditLog> lastAudit =
77
+ SQLEntityRef.on(AuditLog.class, BaseEntityRef.OnDelete.IGNORE).weak();
78
+ ```
79
+
80
+ ## MongoRef<E>
81
+
82
+ For MongoDB entities, use `MongoRef<E>`. The ID type is `String`.
83
+
84
+ ```java
85
+ public class MongoOrderItem extends MongoTenantAware {
86
+
87
+ public static final Mapping ORDER = Mapping.named("order");
88
+ private final MongoRef<MongoOrder> order =
89
+ MongoRef.on(MongoOrder.class, BaseEntityRef.OnDelete.CASCADE);
90
+
91
+ public static final Mapping PRODUCT = Mapping.named("product");
92
+ private final MongoRef<MongoProduct> product =
93
+ MongoRef.on(MongoProduct.class, BaseEntityRef.OnDelete.REJECT);
94
+
95
+ // getters...
96
+ }
97
+ ```
98
+
99
+ `MongoRef` also supports `writeOnceOn()` with the same semantics as `SQLEntityRef`.
100
+
101
+ ## Querying by Reference
102
+
103
+ To filter by a reference field, pass the referenced entity or its ID:
104
+
105
+ ```java
106
+ // By entity
107
+ oma.select(OrderItem.class)
108
+ .eq(OrderItem.ORDER, order)
109
+ .queryList();
110
+
111
+ // By ID
112
+ oma.select(OrderItem.class)
113
+ .eq(OrderItem.ORDER, orderId)
114
+ .queryList();
115
+ ```
116
+
117
+ ## Common Mistakes
118
+
119
+ 1. **Using the wrong ref type** — `SQLEntityRef` is for `SQLEntity` subclasses;
120
+ `MongoRef` is for `MongoEntity` subclasses. Mixing them causes startup errors.
121
+
122
+ 2. **Forgetting OnDelete** — Always choose an explicit `OnDelete` behavior.
123
+ `IGNORE` is rarely correct; it leaves dangling references. Prefer `CASCADE`
124
+ for owned children and `REJECT` for required dependencies.
125
+
126
+ 3. **Mutating a writeOnce ref** — Attempting to change a `writeOnceOn` reference
127
+ after the entity has been persisted throws an exception. This is intentional;
128
+ restructure your logic if you need to change the relationship.
129
+
130
+ 4. **Not declaring refs as final** — Reference fields should be `final`. The
131
+ framework mutates the ref's internal state (ID, cached value) but the ref
132
+ object itself must not be replaced.
133
+
134
+ 5. **Calling getValue() in loops** — Each `getValue()` call may trigger a database
135
+ query. In loops, use batch loading or pre-fetch the related entities.
@@ -0,0 +1,27 @@
1
+ import { ResourceDefinition } from "./loader.js";
2
+ type LayerFilter = string;
3
+ /**
4
+ * Registry of all Sirius framework resource documents.
5
+ *
6
+ * Resources are markdown files organized by layer (kernel, web, db, biz).
7
+ * The registry loads all resources at construction time and provides
8
+ * methods for querying by layer or URI.
9
+ */
10
+ export declare class ResourceRegistry {
11
+ private resources;
12
+ private byUri;
13
+ constructor();
14
+ /**
15
+ * Returns all resources whose layer is included in the given list.
16
+ */
17
+ getResourcesForLayers(layers: LayerFilter[]): ResourceDefinition[];
18
+ /**
19
+ * Returns a single resource by its URI, or undefined if not found.
20
+ */
21
+ getResource(uri: string): ResourceDefinition | undefined;
22
+ /**
23
+ * Returns all registered resources.
24
+ */
25
+ getAllResources(): ResourceDefinition[];
26
+ }
27
+ export {};
@@ -0,0 +1,68 @@
1
+ import { loadResource } from "./loader.js";
2
+ /**
3
+ * Registry of all Sirius framework resource documents.
4
+ *
5
+ * Resources are markdown files organized by layer (kernel, web, db, biz).
6
+ * The registry loads all resources at construction time and provides
7
+ * methods for querying by layer or URI.
8
+ */
9
+ export class ResourceRegistry {
10
+ resources;
11
+ byUri;
12
+ constructor() {
13
+ this.resources = [
14
+ // Kernel (5)
15
+ loadResource("kernel", "di", "Dependency injection patterns: @Part, @Parts, @Register, @ConfigValue"),
16
+ loadResource("kernel", "lifecycle", "Lifecycle interfaces: Startable, Stoppable, Killable"),
17
+ loadResource("kernel", "async", "Async primitives: Tasks, CallContext, Promise, DelayLine"),
18
+ loadResource("kernel", "config", "Configuration system: HOCON, file precedence, framework flags"),
19
+ loadResource("kernel", "commons", "Common utilities: NLS, Strings, Value, Amount, Explain"),
20
+ // Web (3)
21
+ loadResource("web", "controllers", "Web controllers: routing, request handling, response rendering"),
22
+ loadResource("web", "services", "Web services: JSON APIs, ServiceCall, structured endpoints"),
23
+ loadResource("web", "templates", "Pasta/Tagliatelle template engine: syntax, tags, extensions"),
24
+ // DB (5)
25
+ loadResource("db", "entities", "Entity definitions: SQLEntity, MongoEntity, ElasticEntity"),
26
+ loadResource("db", "composites", "Composite pattern: reusable embedded data structures"),
27
+ loadResource("db", "refs", "References and foreign keys: EntityRef, BaseEntityRef"),
28
+ loadResource("db", "queries", "Query API: SmartQuery, filters, pagination, aggregation"),
29
+ loadResource("db", "mixing", "Mixing ORM: Mixins, annotations, schema management"),
30
+ // Biz (11)
31
+ loadResource("biz", "entity-triple", "Entity triple pattern: interface + SQL + Mongo implementations"),
32
+ loadResource("biz", "biz-controller", "BizController base class: tenant-aware controllers"),
33
+ loadResource("biz", "jobs", "Jobs framework: background job definitions, factories, parameters"),
34
+ loadResource("biz", "processes", "Process monitoring: logs, counters, state tracking"),
35
+ loadResource("biz", "importer", "Data import framework: ImportHandler, EntityImportHandler"),
36
+ loadResource("biz", "tenants", "Multi-tenant management: Tenant, UserAccount, permissions"),
37
+ loadResource("biz", "storage", "Object storage: Layer1 (physical), Layer2 (metadata), Layer3 (VFS)"),
38
+ loadResource("biz", "analytics", "Analytics: metrics, events, Clickhouse integration"),
39
+ loadResource("biz", "codelists", "Code lists: lookup tables, managed enumerations"),
40
+ loadResource("biz", "isenguard", "Isenguard: rate limiting, firewall, IP blocking"),
41
+ loadResource("biz", "testing", "Testing patterns: SiriusExtension, test entities, scenarios"),
42
+ ];
43
+ this.byUri = new Map();
44
+ for (const resource of this.resources) {
45
+ this.byUri.set(resource.uri, resource);
46
+ }
47
+ }
48
+ /**
49
+ * Returns all resources whose layer is included in the given list.
50
+ */
51
+ getResourcesForLayers(layers) {
52
+ const layerSet = new Set(layers.map((l) => l.toLowerCase()));
53
+ return this.resources.filter((r) => layerSet.has(r.layer));
54
+ }
55
+ /**
56
+ * Returns a single resource by its URI, or undefined if not found.
57
+ */
58
+ getResource(uri) {
59
+ return this.byUri.get(uri);
60
+ }
61
+ /**
62
+ * Returns all registered resources.
63
+ */
64
+ getAllResources() {
65
+ return [...this.resources];
66
+ }
67
+ }
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAI/D;;;;;;GAMG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,CAAuB;IAChC,KAAK,CAAkC;IAE/C;QACE,IAAI,CAAC,SAAS,GAAG;YACf,aAAa;YACb,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,uEAAuE,CAAC;YACrG,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,sDAAsD,CAAC;YAC3F,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,0DAA0D,CAAC;YAC3F,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,+DAA+D,CAAC;YACjG,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,wDAAwD,CAAC;YAE3F,UAAU;YACV,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,gEAAgE,CAAC;YACpG,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,4DAA4D,CAAC;YAC7F,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,6DAA6D,CAAC;YAE/F,SAAS;YACT,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,2DAA2D,CAAC;YAC3F,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,sDAAsD,CAAC;YACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,uDAAuD,CAAC;YACnF,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,yDAAyD,CAAC;YACxF,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,oDAAoD,CAAC;YAElF,WAAW;YACX,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,gEAAgE,CAAC;YACtG,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,oDAAoD,CAAC;YAC3F,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,mEAAmE,CAAC;YAChG,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,oDAAoD,CAAC;YACtF,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,2DAA2D,CAAC;YAC5F,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,2DAA2D,CAAC;YAC3F,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,oEAAoE,CAAC;YACpG,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,oDAAoD,CAAC;YACtF,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,iDAAiD,CAAC;YACnF,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,iDAAiD,CAAC;YACnF,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,6DAA6D,CAAC;SAC9F,CAAC;QAEF,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,MAAqB;QACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,GAAW;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,189 @@
1
+ # Async and Concurrency
2
+
3
+ Sirius-kernel provides its own concurrency primitives built around managed thread
4
+ pools, context propagation, and a promise/future model.
5
+
6
+ ## Tasks Service
7
+
8
+ `Tasks` is the central service for running background work. It manages named
9
+ executor pools and ensures proper context propagation.
10
+
11
+ ```java
12
+ @Part
13
+ private Tasks tasks;
14
+
15
+ // Run in the default executor
16
+ tasks.defaultExecutor().start(() -> {
17
+ // Background work — CallContext is automatically transferred
18
+ });
19
+
20
+ // Run in a named executor
21
+ tasks.executor("my-pool").start(() -> {
22
+ // Runs in a pool configured under "async.executor.my-pool"
23
+ });
24
+ ```
25
+
26
+ ### Executor Configuration
27
+
28
+ Executors are configured in HOCON:
29
+
30
+ ```hocon
31
+ async.executor.my-pool {
32
+ poolSize = 4
33
+ maxSize = 16
34
+ queueLength = 100
35
+ }
36
+ ```
37
+
38
+ - `poolSize` — Core thread count.
39
+ - `maxSize` — Maximum thread count under load.
40
+ - `queueLength` — Work queue capacity. Tasks beyond this are rejected.
41
+
42
+ ### Periodic Tasks
43
+
44
+ For recurring work, use `EveryMinute`, `EveryTenMinutes`, `EveryHour`, or
45
+ `EveryDay` by implementing the interface and registering:
46
+
47
+ ```java
48
+ @Register(classes = {CleanupTask.class, EveryHour.class})
49
+ public class CleanupTask implements EveryHour {
50
+
51
+ @Override
52
+ public void runTimer() throws Exception {
53
+ // Called every hour
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## CallContext
59
+
60
+ `CallContext` is thread-local context that automatically transfers to child
61
+ threads managed by `Tasks`. It carries:
62
+
63
+ - The current **language** (for NLS translations).
64
+ - The current **user** and **tenant** (MDC-style context).
65
+ - A **TaskContext** for monitoring progress and cancellation.
66
+ - **Log output** and flow tracking.
67
+
68
+ ```java
69
+ // Access current context
70
+ CallContext ctx = CallContext.getCurrent();
71
+
72
+ // Get a sub-context component
73
+ UserInfo currentUser = ctx.get(UserInfo.class);
74
+ ```
75
+
76
+ When `Tasks` dispatches work to a thread pool, it captures the caller's
77
+ `CallContext` and installs it in the worker thread. This means background
78
+ tasks automatically inherit the user, language, and logging context.
79
+
80
+ ### TaskContext
81
+
82
+ Within a `CallContext`, the `TaskContext` tracks the current operation and
83
+ supports cooperative cancellation:
84
+
85
+ ```java
86
+ TaskContext taskCtx = TaskContext.get();
87
+
88
+ // Check if the current task should stop
89
+ if (!taskCtx.isActive()) {
90
+ return;
91
+ }
92
+
93
+ // Report progress
94
+ taskCtx.setState("Processing item %d of %d", current, total);
95
+ ```
96
+
97
+ Long-running operations should periodically check `isActive()` to support
98
+ graceful cancellation.
99
+
100
+ ## Promise and Future
101
+
102
+ Sirius provides its own `Promise<T>` as a lightweight alternative to
103
+ `CompletableFuture`:
104
+
105
+ ```java
106
+ // Create a promise
107
+ Promise<String> promise = new Promise<>();
108
+
109
+ // Register handlers
110
+ promise.onSuccess(value -> {
111
+ // Called when the promise is fulfilled
112
+ });
113
+ promise.onFailure(error -> {
114
+ // Called when the promise fails
115
+ });
116
+
117
+ // Fulfill or fail
118
+ promise.success("result");
119
+ // or
120
+ promise.fail(new IOException("failed"));
121
+ ```
122
+
123
+ ### Blocking Wait
124
+
125
+ ```java
126
+ String result = promise.await(Duration.ofSeconds(30));
127
+ ```
128
+
129
+ `await()` blocks until the promise completes or the timeout expires.
130
+
131
+ ### Chaining
132
+
133
+ ```java
134
+ Promise<Integer> length = promise.map(String::length);
135
+ ```
136
+
137
+ ## DelayLine
138
+
139
+ `DelayLine` provides delayed execution — tasks are submitted and executed after
140
+ a configured delay:
141
+
142
+ ```java
143
+ @Part
144
+ private DelayLine delayLine;
145
+
146
+ delayLine.callDelayed("my-token", Duration.ofMinutes(5), () -> {
147
+ // Executed 5 minutes from now
148
+ });
149
+ ```
150
+
151
+ The token parameter is used for deduplication — submitting a new task with the
152
+ same token cancels the previous one. This is useful for debouncing (e.g., only
153
+ send a notification if no further events arrive within 5 minutes).
154
+
155
+ ## Orchestration
156
+
157
+ `Orchestration` coordinates distributed background work across cluster nodes.
158
+ It ensures that periodic tasks run on only one node in a cluster:
159
+
160
+ ```java
161
+ @Register(classes = {MyDistributedTask.class, EveryHour.class})
162
+ public class MyDistributedTask implements EveryHour {
163
+
164
+ @Part
165
+ private Orchestration orchestration;
166
+
167
+ @Override
168
+ public void runTimer() throws Exception {
169
+ orchestration.runInCluster("my-task", () -> {
170
+ // Only executes on one node
171
+ });
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Best Practices
177
+
178
+ 1. **Always use `Tasks`** for background work — never create raw threads.
179
+ `Tasks` ensures context propagation and proper executor management.
180
+
181
+ 2. **Check `isActive()`** in long-running loops to support cancellation.
182
+
183
+ 3. **Use DelayLine** for debounce patterns instead of `Thread.sleep`.
184
+
185
+ 4. **Configure executor pools** explicitly. The default pool is shared and
186
+ should not be saturated with long-running tasks.
187
+
188
+ 5. **Name your executors** descriptively. Executor names appear in thread dumps
189
+ and monitoring, making debugging much easier.