Open fluffy opened 7 months ago
IMO we should negotiate extensions in the SETUP message. I'm a little worried about adding arbitrary blobs to messages that might change the relay's behavior, although I understand that's how HTTP works today.
+1 on both parts of what you said. Let me give a more concrete example.
Many systems end up traces defined roughly like https://opentelemetry.io/docs/concepts/signals/traces/ - to debug things across clients, servers, and a distributed system. To make this work for MoQT, we need to be able to add a trace id that is sent across the network. In many cases not every object would be traced as that would have a performance impact, but perhaps the first object in all the I frames might be traced from a client that was having issues.
I was imaging something like a spec could define an extension and get a IANA number for it ( small numbers for RFC, larger numbers for non IETF specs ). You would negotiate support for the extension in usual way in the SETUP. Then in the object there would be an array of extensions that could be zero size and each the data for each extension included the number that identified the extension. That way someone could right an extension for something like adding a trace_id to object and relays that supported that could log information about it that included the trace-id.
We probably need a way for extensions to mandatory to understand or not by relays and if not mandatory to understand include a length or way to be able to skip over the data.
Thanks Cullen for raising this issue.
I think Will had some use cases that needed it
Indeed I do. I was thinking about the usefulness of headers in HTTP and how much innovation, behavior and extensibility in workflows is enabled by their presence and relatively simple implementation. I'd like something similar for moqt.
there was a way to indicate if the header needs to be understood by relays, reviewers, both, or neither.
IMO, if a header needs to be understood by relays, then it should be part of the moqt specification and not part of some extensibility scheme. Therefore I'd say that headers are NEVER mandatory to be understood by relays. Headers, if present, are to be understood only by those nodes that know to look for them. All other nodes would ignore them.
IMO we should negotiate extensions in the SETUP message. I'm a little worried about adding arbitrary blobs to messages that might change the relay's behavior, although I understand that's how HTTP works today.
If we go with headers are never mandatory, then there is no need for negotiation during setup. I'd rather the existing subprotocol negotiation handle required behavior.
For extensibility, we can go in two directions. The first, as suggested, is an IANA table for each header type.
OBJECT_STREAM Message {
Subscribe ID (i),
Track Alias (i),
Group ID (i),
Object ID (i),
Object Send Order (i),
Custom-extension1 (i),
Custom-extension2 (i),
Object Payload (..),
}
The second , which I prefer, defines a single new Object header called "header", or perhaps "Extension" if we wish to decouple it from the HTTP semantics.
OBJECT_STREAM Message {
Subscribe ID (i),
Track Alias (i),
Group ID (i),
Object ID (i),
Object Send Order (i),
Extension(i),
Object Payload (..),
}
The Extension bytes are then a concatenation of Extension types|length|payloads. The types are identifiers that are registered with IANA. Order is unimportant.
Extension {
Extension type (i),
Extension length (i),
Extension payload (i),
Extension type (i),
Extension length (i),
Extension payload (i),
...
Extension type (i),
Extension length (i),
Extension payload (i)
}
A node can ignore the Extension filed completely, or it may parse its way down the extension payload looking for extension types that it recognizes. Once it recognizes a type, it will know how to parse the payload.
+1 on the the TLV approach in your second approach with Extension.
Just one little tweek to your syntax above ... I think the object would need to look more like
Num Extensions (i),
Extension(..),
It seems to me you need to know the number of them to know when to stop parsing. I don't know if the style is better to have that outside or inside the Extension structure but somewhere.
It seems to me you need to know the number of them to know when to stop parsing.
I think it would work without, because the Extension(i) value in the object header would give the total number of bytes assigned to the extensions. 0 indicates no extensions. For a positive value B, any receiver could parse their way down until totalExtensionBytesProcessed = B and then they would know that the next byte represents the start of the Object payload and not the next TLV extension.
I would like to see specific use cases presented for this before discussing further.
IMO, if a header needs to be understood by relays, then it should be part of the moqt specification and not part of some extensibility scheme. Therefore I'd say that headers are NEVER mandatory to be understood by relays. Headers, if present, are to be understood only by those nodes that know to look for them. All other nodes would ignore them.
I'm not sure this makes sense to me. If a specific piece of metadata does not need to be understood by relays, you can just put it into the object payload.
If a specific piece of metadata does not need to be understood by relays, you can just put it into the object payload.
But what if some relays need to use it while the rest can ignore and forward it? Here's an example: imagine a advertising header that only the outermost relay ( the edge relay) will use to manipulate data that it sends to the end subscribers. We can't put this info in the object payload because that is opaque to relays and may be encrypted. Therefore we would like to have this extension header be preserved and forwarded (but not understood or parsed) by distribution relays and then only the last relay, which is programmed to look for it, would actually parse and use it.
Individual Comment:
The original issue says:
we need a way of adding header to an object that the relay can read.
and
to be understood by relays, receivers*, both, or neither
What is the function of adding an extension header at the MoQT level if no relay acts on it? Shouldn't such fields be defined in the streaming format layer, and carried in MoQT payloads?
*OP said "reviewers" but I assume meant receivers.
Tianji and I posted comments related to MoQT object header extensibility in #502
Description of the 5G XR use case: https://github.com/moq-wg/moq-transport/pull/502#issuecomment-2471516973
Additional details about requirements for this use case: https://github.com/moq-wg/moq-transport/pull/502#issuecomment-2479800594
What is the function of adding an extension header at the MoQT level if no relay acts on it? Shouldn't such fields be defined in the streaming format layer, and carried in MoQT payloads?
@afrind - the reality is that MOQT traffic will move through a number of intermediaries between original publisher and final subscriber. Most of these will be standard distribution relays of the type we envisage CDNs deploying. However other intermediaries may serve special roles within 5G networks, or enterprise networks, or networks we have not imagined yet. For these we may want to provide relay-accessible metadata via headers to improve MoQT performance, security or economics. To satisfy these use-cases we have two choices: we can hard-code these headers in to every MOQT object, which bloats our spec with a bunch of headers which are rarely used by most relays, or we can create extensions so that only those use-cases which leverage those headers need deploy them. The latter path seems more practical.
Another argument for extensions is flexibility in evolution. We may want to experiment with new headers for new purposes, even for standard distribution relays. Forcing a spec update to the complete MOQT spec just add an experimental header is a heavy uplift and one we would like to do as infrequently as possible. By decoupling headers from the spec we allow a CDN to deploy a fixed version of MOQT and then allow content distributors to experiment on top of it. This will allow faster improvement of MOQT over time.
See also https://github.com/moq-wg/moq-transport/issues/433#issuecomment-2273801886
Individual Comment:
@wilaw I wasn't trying to argue against extensions in general, only that I think the bar for defining a MoQT extension ought to be the ability to specify what a relay that negotiates support for that extension does with the information. If the answer is only "forward unmodified", then the extension isn't an moqt extension at all, it's just part of a higher level protocol and should be conveyed there -- in the MoQT payload.
the ability to specify what a relay that negotiates support for that extension does with the information
@afrind - the current PR has language that relays MUST not modify these extension headers and MUST propagate them. What then is the purpose of negotiating "support"? What does support even mean? Since the extensions are generated by the original publisher and not intermediaries, the relay can't guarantee that they are going to be present because it doesn't create them and it is already bound to forward all extension headers that it receives. I feel we could remove the whole SETUP negotiation handshake around extensions and simplify extensions down to the point where a) we define their structure and how to add them to an Object and b) we require relays to propagate them unmodified if present.
The whole draft needed a pass at some point for extensibility but a more immediate concern is to play with various things, we need a way of adding header to an object that the relay can read. The LOC draft has a line about needing this, I think Will had some use cases that needed it, and it would help with experimenting and migrating some pre standard implementations to the current version of the draft.
The requirement would be there was a way to define new headers in extensions, there was a way to indicate if the header needs to be understood by relays, reviewers, both, or neither.