fabrics plan and easy fixes

This commit is contained in:
Dmitry Sergeev
2026-09-02 01:06:40 +03:00
parent e81dda3b3f
commit 6058d3ae64
2 changed files with 224 additions and 6 deletions
+224
View File
@@ -0,0 +1,224 @@
# MXL Fabrics ingress plan
## Goal
Receive remote MXL audio and video through `go-mxl/fabrics`, place the received data into local MXL storage, and feed it into the existing player without changing playback, retry, session, synchronization, playlist, renderer, or audio-output semantics.
Start with the SHM provider. TCP, verbs, and EFA follow after the lifecycle and media paths are stable.
## Correct architecture
`go-mxl/fabrics` is not a second reader backend. A Fabrics target receives remote transfers into memory owned by a local `mxl.Writer`:
```text
remote fabrics.Initiator
|
| remote transfer
v
fabrics.Target
|
| writes into a local mxl.Writer ring
v
local MXL flow
|
v
VideoFrame / AudioSamples -> playback workers -> output
```
Consequences:
- the local MXL media path remains the reference implementation;
- Fabrics is an ingress/transport concern, not a playback backend;
- playback and playlist code must not know about providers or endpoints;
- each remotely transferred flow requires its own target;
- target completions must be matched to the exact local grain/sample range;
- do not add a blind polling loop that could return stale or overwritten media.
## Confirmed API facts
The project pins `github.com/qvest-digital/go-mxl v1.1.0-rc.1`. It includes `github.com/qvest-digital/go-mxl/fabrics` and providers `shm`, `tcp`, `verbs`, `efa`, and `any`.
The receive-side flow is:
1. create a local `mxl.Instance`;
2. create an `mxl.Writer` from the flow definition;
3. create a `fabrics.Instance` from the local MXL instance;
4. create a `fabrics.Target`;
5. call `Target.Setup` with provider, node/service, writer, and options;
6. publish the returned `TargetInfo` to the remote initiator;
7. consume completions with `ReadGrain` or `ReadSamples`.
`TargetInfo` is control-plane data. Initially serialize it to a file for the known sender; automatic discovery is out of scope.
`fabrics.ErrNotReady` means no completion is ready before the timeout. `fabrics.ErrInterrupted` can be caused by Go runtime signals and means retry the read operation. Neither is a feed failure by itself.
## Current native-library state
The installed pkg-config metadata reports `libmxl 1.2.0.0` and `libmxl-fabrics 1.2.0.0`. The pinned Go module records native MXL `v1.1.0-rc1` as its corresponding version. Resolving `libmxl-fabrics` currently fails because pkg-config cannot find `libfabric`.
This does not prove 1.2 is incompatible. Compare the stack with the user's known-working C++ sender/receiver and choose one tested version set. Do not conceal ABI/API mismatches with casts or copied declarations.
## Configuration boundary
Fabrics code should live in `internal/adapter/mxlfabrics`. A provisional configuration is:
```go
type IngressConfig struct {
Provider fabrics.Provider
Node string
Service string
Options json.RawMessage
TargetInfoPath string
Flow mxl.FlowDefinition
}
```
Use the exact flow-definition type exposed by the selected library version. Video and audio have independent configurations and targets. They may use different remote endpoints/providers while landing in the same local MXL domain.
Do not add `--backend local|fabrics`. The useful distinction is whether a feed is already local or needs Fabrics ingress. Choose final per-feed CLI names after the configuration spike.
## Implementation stages
### Stage 0 — Align and prove the native stack
- Record exact versions/commits of `libfabric`, `libmxl`, `libmxl-fabrics`, and `go-mxl` used by the working C++ pair.
- Decide whether to retain Go/native 1.1 RC or move the complete set together.
- Make all pkg-config and dynamic-linker checks pass.
- Run the known C++ pair, then the matching upstream Go SHM target/initiator examples.
- Enumerate interfaces/providers through the Go API.
- Preserve the current player test baseline.
```sh
pkg-config --modversion libfabric libmxl libmxl-fabrics
pkg-config --cflags --libs libfabric libmxl libmxl-fabrics
ldd /usr/local/lib/libmxl-fabrics.so
go test ./...
```
Acceptance: one documented compatible version set; repeatable Go SHM transfer; clean cancellation/shutdown; no player production changes.
### Stage 1 — Minimal SHM ingress spike
- Create a local MXL instance and writer from a supplied flow definition.
- Set up one SHM target and serialize its `TargetInfo` to an explicit file.
- Receive video grains and audio sample ranges from the known sender in isolated experiments.
- Verify completion indices against the corresponding local ring contents.
- Document buffer lifetime, overwrite behavior, and sender-absent behavior.
Acceptance: video and audio transfers work independently; no busy loop; shutdown promptly releases resources.
### Stage 2 — Define the ingress lifecycle
Create a narrow adapter owned by the composition root. It must:
- validate before allocating native resources;
- create the writer before target setup and publish info only after setup;
- classify timeouts, interruptions, cancellation, and terminal errors;
- remain context-cancellable although native reads use timeouts;
- allow only one completion reader per target;
- close every resource exactly once and unwind partial setup safely.
Confirm the precise close order against the chosen version. Expected order:
1. cancel and join the completion loop;
2. close target;
3. close returned `TargetInfo`;
4. close the Fabrics instance after all targets;
5. close local readers/writers while respecting the target's writer reference;
6. close the local MXL instance last.
Acceptance: allocation-failure tests, prompt idle/active cancellation, race-safe repeated start/stop, and no resource leaks.
### Stage 3 — Video ingress
- Receive completion indices and obtain the exact completed local grain.
- Map metadata/payload into `playback.VideoFrame`.
- Preserve borrowed-buffer lifetime, or make one deliberate bounded copy if required for safety.
- Reject unsupported formats explicitly.
- Track received, delivered, overwritten, and malformed grains.
Never independently poll for the newest local frame after a completion; it may be a different index.
Acceptance: correct V210 playback; monotonic indices except documented restart; stable 25/50/59.94/high-rate playback; clean disappearance/restoration.
### Stage 4 — Audio ingress
- Use `ReadSamples` completion ranges and read exactly the reported head/count.
- Map rate, channels, format, label, UUID, and index into the audio contract.
- Preserve SDL backpressure and queue policy.
- Track samples, discontinuities, overruns, and queue depth.
Acceptance: correct mono/stereo/multichannel and common rates; no corruption/drift; no duplicated/skipped ranges; no stale queued audio after stop/reconnect.
### Stage 5 — Independent and synchronized A/V
Independent A/V uses two independent targets/lifecycles; one failure must not stop the other.
For synchronized A/V:
- land both flows in a compatible local MXL domain;
- retain native local-MXL synchronization where supported;
- coordinate completion availability so sync never consumes data not yet received;
- verify different endpoints/providers and fractional frame rates such as 59.94.
Fabrics transports media; it is not a reason to invent manual synchronization. If native synchronization cannot safely use received rings, write a separate sync design first.
Acceptance: failure isolation when independent; one retry lifecycle when synchronized; runtime sync toggle retains current semantics; long playback stays aligned.
### Stage 6 — Player integration
- Add per-feed local/Fabrics transport configuration.
- Construct ingress outside `internal/playback` and connect it to existing workers/slots.
- Preserve stop, resume, replace, remove, sync toggle, retry, and playlist behavior.
- Keep playlist entries provider-neutral where possible.
- Add provider, endpoint, ingress state, completion age, and counters to stats.
Acceptance: local behavior is unchanged; Fabrics supports video-only, audio-only, independent A/V, and sync A/V; bad config fails before native goroutines; feeds remain independently controllable.
### Stage 7 — Provider rollout
Enable and validate in order:
1. SHM;
2. TCP;
3. verbs;
4. EFA if deployment requires it.
Provider selection should affect only Fabrics interface configuration and deployment prerequisites. Playback controllers must not branch on provider names.
### Stage 8 — Playlist and resilience validation
Test empty startup; audio/video only; independent and synchronized A/V; either sender disappearing/restoring; finite/infinite retry; stop/resume/remove/replace; sync toggling during playback/backoff; `wait`/`next` playlists; timed looping/manual navigation; 59.94/high-rate video; and repeated long-running startup/shutdown.
Record CPU, memory, received/displayed FPS, completion delta, dropped/overwritten media, audio queue, reconnect latency, and native resource counts.
## Test layers
1. Unit tests for configuration, error classification, and lifecycle transitions.
2. Adapter tests with injected wrappers around native allocations and reads.
3. Real SHM initiator/target integration tests.
4. Player smoke tests for all playback combinations.
5. Race and repeated lifecycle tests.
6. Provider-specific TCP/verbs/EFA live tests.
Native/live tests must be explicit integration tests and skip with a useful reason when prerequisites are absent.
## Out of scope for the first pass
- player as Fabrics initiator/sender;
- automatic endpoint or TargetInfo discovery;
- changing provider on an active target;
- manual A/V sync without a reviewed design;
- new media formats bundled into transport work;
- provider-specific logic in playback, playlist, renderer, or GUI;
- unsafe workarounds for native version mismatches.
## First action tomorrow
1. Build/install the exact known-working native stack.
2. Record every native version/commit and the C++ reference commands.
3. Make all pkg-config checks pass.
4. Run the C++ sender/receiver baseline.
5. Run the matching upstream Go SHM target/initiator example.
6. Capture its flow definition and `TargetInfo` exchange.
7. Start Stage 1 only when transfer and clean shutdown are repeatable.
-6
View File
@@ -97,12 +97,6 @@ func checkMXLargs(args appArgs) {
} }
func main() { func main() {
// timelapse video: 5fbec3b1-1b0f-417d-9059-8b94a47197ed
// timelapse audio: 5fbec3b1-1b0f-417d-9059-8b94a47197ec
// f1 video: 5fbec3b1-1b0f-417d-9059-8b94a47197ef
// f1 audio: 5fbec3b1-1b0f-417d-9059-8b94a47197eb
// sync video: 2618979d-76a5-45e0-83cb-0f192978d1cd
// sync audio: 9d2a041b-01cf-4ee4-bffa-188fe093c99b
var args appArgs var args appArgs
flagSet := pflag.NewFlagSet(APP_NAME, pflag.ContinueOnError) flagSet := pflag.NewFlagSet(APP_NAME, pflag.ContinueOnError)
flagSet.SortFlags = false flagSet.SortFlags = false