You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Party/Place resources: follow the same convention if ambiguity is possible.
167
+
168
+
E.g. `Diffo.Test.Instance.CardInstance` → label `:CardInstance`,
169
+
and `Diffo.Test.Characteristic.CardCharacteristic` → label `:CardCharacteristic` — no collision.
170
+
146
171
## Common agent mistakes
147
172
148
173
- Using old `structure do` / top-level `instances do` — use `provider do` only.
149
174
- Using `party :role, Type, reference: true` — use `party_ref :role, Type`.
150
175
- Using a plain `Ash.TypedStruct` as a `characteristic` DSL target — use a `BaseCharacteristic`-derived resource instead; the TypedStruct belongs in `<Module>.Value`.
176
+
- Using `characteristic :name, Diffo.Provider.AssignableCharacteristic` for pools — use `pools do / pool :name, :thing / end` instead.
177
+
- Using the removed `AssignableValue` TypedStruct — it no longer exists; use `pools do`.
178
+
- Calling `Assigner.assign/4` when a `pools do` declaration exists — prefer `Assigner.assign/3` which looks up the thing automatically.
179
+
- Forgetting to call `Pool.update_pools/3` in `:define` actions when the resource has `pools do` — pool bounds (`first`, `last`, `algorithm`) are set here.
180
+
- Using `characteristic :pool_name, Diffo.Provider.AssignedToRelationship` — `AssignedToRelationship` is not a characteristic; use `pools do / pool :name, :thing / end` instead.
181
+
- Querying `Diffo.Provider.Relationship` for assignment records — assignment relationships are on `Diffo.Provider.AssignedToRelationship`; access them via `instance.assignments`.
182
+
- Filtering `instance.forward_relationships` for `type == :assignedTo` — those records no longer exist there; use `instance.assignments` directly.
151
183
- Calling `build_before/1` or `build_after/2` in actions — these run automatically.
152
184
- Declaring `:specified_by`, `:features`, `:characteristics` as action arguments.
153
185
- Editing `documentation/dsls/DSL-Diffo.Provider.Extension.md` — it is Spark-generated;
Copy file name to clipboardExpand all lines: documentation/dsls/DSL-Diffo.Provider.Extension.md
+50Lines changed: 50 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,6 +30,10 @@ the sections relevant to it, and verifiers enforce correct usage.
30
30
end
31
31
end
32
32
33
+
pools do
34
+
pool :ports, :port
35
+
end
36
+
33
37
parties do
34
38
party :provider, MyApp.Provider
35
39
party_ref :owner, MyApp.InfrastructureCo
@@ -92,6 +96,8 @@ Provider DSL — structure, roles, and behaviour for this resource kind
92
96
*[features](#provider-features)
93
97
* feature
94
98
* characteristic
99
+
*[pools](#provider-pools)
100
+
* pool
95
101
*[parties](#provider-parties)
96
102
* party
97
103
* parties
@@ -276,6 +282,50 @@ Adds a Characteristic
276
282
277
283
278
284
285
+
### provider.pools
286
+
Assignable pools on this Instance — each pool maps to an AssignableCharacteristic
287
+
288
+
### Nested DSLs
289
+
*[pool](#provider-pools-pool)
290
+
291
+
292
+
### Examples
293
+
```
294
+
pools do
295
+
pool :ports, :port
296
+
end
297
+
298
+
```
299
+
300
+
301
+
302
+
303
+
### provider.pools.pool
304
+
```elixir
305
+
pool name, thing
306
+
```
307
+
308
+
309
+
Declares an assignable pool — a named range of values for auto-assignment
310
+
311
+
312
+
313
+
314
+
315
+
### Arguments
316
+
317
+
| Name | Type | Default | Docs |
318
+
|------|------|---------|------|
319
+
|[`name`](#provider-pools-pool-name){: #provider-pools-pool-name .spark-required} |`atom`|| The pool name (matches the AssignableCharacteristic name). |
320
+
|[`thing`](#provider-pools-pool-thing){: #provider-pools-pool-thing .spark-required} |`atom`|| The name of the thing being assigned within the pool (e.g. :port). |
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
279
329
### provider.parties
280
330
Party roles on this resource — `party`/`parties`/`party_ref` for Instance kinds; `role` for Party and Place kinds
Copy file name to clipboardExpand all lines: documentation/how_to/use_diffo_provider_extension.livemd
+31-31Lines changed: 31 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,8 @@ The id is a stable UUID4, the same in every environment for this Instance kind.
141
141
142
142
**`features do`** — optional capabilities with their own typed characteristic payload.
143
143
144
+
**`pools do`** — assignable pools for partial resource allocation. Each `pool :name, :thing` declaration creates an `AssignableCharacteristic` node during `build` and generates `pools/0` / `pool/1` on the module. Pool bounds (`first`, `last`, `algorithm`, `assignable_type`) are set in a `:define` action via `Pool.update_pools/3`. Assignment actions use `Assigner.assign/3` — the thing name is looked up from the pool declaration.
145
+
144
146
**`parties do`** — party roles: `party` (singular), `parties` (plural), `party_ref` (reference, no direct edge).
@@ -151,7 +153,7 @@ arguments automatically onto that action.
151
153
152
154
Each characteristic is a dedicated Ash resource using the `Diffo.Provider.BaseCharacteristic` fragment. It carries direct typed attributes and a `:value` calculation that builds a companion `<Module>.Value` TypedStruct for ordered JSON encoding. The TypedStruct uses [AshJason.TypedStruct](https://hexdocs.pm/ash_jason/) to control field order in the JSON output.
153
155
154
-
For partial resource allocation and assignment we've created Diffo.Provider.Assigner. It is used by the host resource, which declares a characteristic with a `Diffo.Provider.AssignableValue` TypedStruct. Allocation is managed within the Provider domain using this characteristic. Assignment to Services or Resources is via 'reverse' type: "assignedTo" relationships enriched by relationship characteristics.
156
+
For partial resource allocation and assignment we've created `Diffo.Provider.Assigner`. The host resource declares a `pools do` section listing each assignable pool by name and the kind of thing being assigned. Pool bounds (first/last value, algorithm) are set via a `:define` action. Each assignment is stored as a `Diffo.Provider.AssignedToRelationship` node (Neo4j label `:AssignmentRelationship`) carrying `pool`, `thing`, and the `assigned` value. These are distinct from regular TMF `Diffo.Provider.Relationship` nodes and are accessible on an instance via `instance.assignments`.
155
157
156
158
Let's imagine a Compute domain which operates GPU and NPU resources. We want to expose a Cluster composite resource which can be dynamically composed of a number of GPU and NPU cores.
157
159
@@ -381,7 +383,7 @@ defmodule Diffo.Compute.GpuCharacteristic.Value do
381
383
end
382
384
```
383
385
384
-
The GPU resource declares `GpuCharacteristic` for the typed `:gpu` slot and keeps `AssignableValue` for the `:cores`allocation pool (the assigner still uses the dynamic characteristic pattern). The `update :define` action now only needs to handle the dynamic `:cores` update — the typed `:gpu` characteristic is updated directly on the characteristic resource:
386
+
The GPU resource declares `GpuCharacteristic` for the typed `:gpu` slot and uses `pools do` to declare the `:cores`assignable pool. The `update :define` action updates both the typed characteristic and the pool bounds. The `update :assign_core` action uses `Assigner.assign/3`— the thing name (`:core`) is looked up from the pool declaration automatically:
385
387
386
388
```elixir
387
389
defmoduleDiffo.Compute.GPUdo
@@ -391,10 +393,10 @@ defmodule Diffo.Compute.GPU do
391
393
392
394
aliasDiffo.Provider.BaseInstance
393
395
aliasDiffo.Provider.Instance.Relationship
394
-
aliasDiffo.Provider.Instance.Characteristic
396
+
aliasDiffo.Provider.Extension.Characteristic
397
+
aliasDiffo.Provider.Extension.Pool
395
398
aliasDiffo.Provider.Assigner
396
399
aliasDiffo.Provider.Assignment
397
-
aliasDiffo.Provider.AssignableValue
398
400
aliasDiffo.Compute
399
401
aliasDiffo.Compute.GpuCharacteristic
400
402
@@ -418,7 +420,10 @@ defmodule Diffo.Compute.GPU do
418
420
419
421
characteristics do
420
422
characteristic :gpu, GpuCharacteristic
421
-
characteristic :cores, AssignableValue
423
+
end
424
+
425
+
pools do
426
+
pool :cores, :core
422
427
end
423
428
424
429
behaviour do
@@ -442,11 +447,13 @@ defmodule Diffo.Compute.GPU do
442
447
end
443
448
444
449
update :definedo
445
-
description "allocates the GPU cores (AssignableValue)"
450
+
description "sets GPU identity and allocates the cores pool"
We set the typed `:gpu` characteristic directly on the characteristic resource, then allocate the `:cores` AssignableValue via `update :define`:
754
+
We define each GPU: setting its typed `:gpu` characteristic fields and allocating the `:cores` pool bounds. Both are passed via `characteristic_value_updates` to the `:define` action — `Characteristic.update_all` handles the typed `:gpu`update and `Pool.update_pools` handles the `:cores` pool bounds:
749
755
750
756
```elixir
751
-
# Update the typed GpuCharacteristic on each GPU
752
-
[gpu_char_1] =Enum.filter(gpu_1.characteristics, fn c -> c.name ==:gpuend)
753
-
[gpu_char_2] =Enum.filter(gpu_2.characteristics, fn c -> c.name ==:gpuend)
The GPU's `:cores`characteristic is an AssignableValue that tracks how many cores are free (unassigned). We can render one as json:
766
+
The `:cores`pool is backed by an `AssignableCharacteristic` node that records the range bounds and algorithm. Free cores are computed at assignment time from the count of existing `AssignmentRelationship` records — there is no stored `free` counter. We can render one as json:
Now our cluster should have a core from each gpu. Check in the neo4j browser for the type: :assignedTo Relationship from the gpu_1 and gpu_2 to the clusters. There should be four, each with a Relationship Characteristic of core, with a value of the assigned core, e.g. 1, 2.
786
+
Now our cluster should have a core from each GPU. Check in the Neo4j browser for `:AssignmentRelationship` nodes from `gpu_1` and `gpu_2` to `cluster_1`. There should be four — each carries `pool: :cores`, `thing: :core`, and the `assigned` integer value (e.g. 1, 2, 3 from gpu_1 and 1 from gpu_2).
789
787
790
-
Also the gpu will show each assignedTo relationship, since these are forward relationships. These should also show the relationship characteristic:
788
+
The GPU's `assignments` hold each assignment, showing the assigned core number in the JSON encoding as a `resourceRelationshipCharacteristic`:
791
789
792
790
```elixir
793
791
Jason.encode!(gpu_1, pretty:true) |>IO.puts
794
792
```
795
793
796
-
Make sure you have a look at it in the neo4j browser. There should be Relationship nodes with a role of :assignedTofrom each GPU resource instance to the cluster_1 resource instance. Each Relationship should be defined by a Characteristic with the assigned core number.
797
-
There is no central assignment table, rather the relationships ARE the assignments.
794
+
Make sure you have a look at it in the neo4j browser. There should be `:AssignmentRelationship` nodes from each GPU resource instance to the `cluster_1` resource instance, each carrying the assigned core number.
795
+
There is no central assignment table — the `AssignedToRelationship` nodes ARE the assignments. They are separate from the regular `:Relationship` nodes used for TMF service/resource relationships, and are accessible in Elixir via `instance.assignments`.
798
796
799
797
As an exercise, clone the GPU resource to create an NPU resource and assign some NPU cores from it to your cluster. Check that the assigned NPU cores are unique.
800
798
@@ -805,7 +803,9 @@ What happens when I request a specific assignment from an instance to which the
805
803
806
804
In this tutorial you've used Diffo's unified `provider do` extension to define a Compute domain with:
807
805
808
-
- A composite Cluster resource with GPU core assignment via `Diffo.Provider.Assigner`
806
+
- A composite Cluster resource that receives GPU cores via `Diffo.Provider.Assigner`
807
+
- A GPU resource using `pools do` to declare the `:cores` assignable pool — `pool :cores, :core` replaces the old `characteristic :cores, AssignableValue` pattern
808
+
- Assignments stored on `Diffo.Provider.AssignedToRelationship` nodes (Neo4j label `:AssignmentRelationship`, distinct from TMF `:Relationship` nodes); accessible via `instance.assignments`
809
809
-`Tenant` and `Engineer` Party kinds declared with `provider do` that express which instances they operate and manage
810
810
- A `DataCentre` Place kind that declares the instances located at it
0 commit comments