- From: Harald Alvestrand <harald@alvestrand.no>
- Date: Fri, 28 Sep 2012 11:54:39 +0200
- To: Martin Thomson <martin.thomson@gmail.com>
- CC: public-webrtc@w3.org
On 09/27/2012 06:35 PM, Martin Thomson wrote:
> On 27 September 2012 07:49, Harald Alvestrand <harald@alvestrand.no> wrote:
>> (back to being serious on this thread, and top-posting because I want to
>> talk about general principles rather than details of a proposal..)
>>
>> My experience with stats systems (mostly SNMP, but also mrtg, nagios and a
>> couple of Google-internal systems) is that they generally descend to work on
>> basic items, and offer powerful graphing, summarization and alarming
>> features that work from these primitive values - but rarely, if ever, have I
>> found an interface where it's comfortable to work with structured values
>> directly.
> That is a concern that I can understand. The only systems that I've
> seen work for any amount of structure are in reality tightly coupled,
> even if the interface is MIB. The problem as I see it arises from
> having too large a surface area, which inevitably results in a
> mismatch between what the two sides need. Compromises follow.
>
>> I *like* structured objects. They are extremely useful in many ways,
>> especially when thinking about things, but they are hard to handle. In
>> particular, in the WebIDL type system, having functions that return "either
>> an object or an array or a dictionary" is tricky.
> Actually that's untrue:
> any getValue(DOMString key);
Except that Webkit's IDL doesn't support "any" in any meaningful sense...
>
> Or with union types:
> (DOMString or double) getValue(DOMString key);
>
> That might have consequences for a strongly typed language that
> implements the IDL. But let's be honest, we're talking about
> Javascript here - it's just not a problem.
>
>> Martin's mention of "JSON pointer" (I assume that this is
>> draft-ietf-appsawg-json-pointer-03.txt) makes me think that we might be able
>> to have our cake and eat it... if we define just 2 operations:
>>
>> - getValue("identifier") -> primitive object
>> - getNames("identifier") -> list of names (sequence<DOMString>) that are
>> valid for the next level down
> I assume that getValue("") would enumerate the top of the tree (""
> being the empty JSON pointer that identifies the root, as opposed to
> "/" which identifies a node with a key of the empty string)?
Are you assuming that getValue("") would function like getNames("")?
I'm not sure what you mean here.
>
> The problem that I have with this is that - at a practical level - I
> can't distinguish between:
>
> object.getValue("/a/b/3"); // === 12
> object.getNames("/a/b").map(object.getValue.bind(object)); // === [
> "A", "b", "C", "d" ]
> and
> object["a"]["b"][3]; // === 12
> object["a"]["b"]; // === [ "A", "b", "C", "d" ]
>
> Note the added complexity involved in enumerating values in your proposal.
Adam Barth told me that having attributes as arrays implies that you can
modify those attributes. They're pass-by-reference, not pass-by-value.
This isn't what we want; it makes no semantic sense to have stats be
modifiable.
So my current implementation has gone from arrays to functions returning
sequence<object> instead.
(The fact that array-valued attributes of complex types don't seem to
work with the current WebKit is a different matter. I won't explore that
just now.)
The other thing I don't like about this is
>
> I'm less convinced of the need for JSON pointer in this case. Most
> uses for JSON pointer involve JSON documents where the pointer exists
> outside the document, or there is a need within the document to have
> DRY (don't repeat yourself) complex relationships (multiple owners for
> the same structure, etc...). In an object model, you can easily build
> those complex relationships if it is really necessary.
>
> But this is a theoretical discussion, we need the object model before
> we can determine which of the available options are appropriate. We
> may well disagree on the finer points, but without having the big
> picture laid out, we're really just guessing.
>
>> we can represent any level of complexity, and allow navigation through it,
>> without having to deal with compound values.
>> (Of course, we can also allow getValue to return compound values - if anyone
>> cares enough to get Webkit and friends to understand that.....)
>>
>> The objects I see are:
>>
>> - SSRCs (one or more per MediaStreamTrack - that is, an N:1 mapping)
>> - Transports (1:N mapping to SSRCs)
>> - Components (RTP / RTCP ... where does DTLS fit?)
>> - Candidate pairs (N:1 mapping to transports)
>> - Candidates (N:N mapping to candidate pairs??)
> Building an object model is the first and most important task in any
> operational task. If the model is poor, then nothing you produce will
> ever really fit and your users will end up having to write more code
> to fix any shortcomings. This looks like a good start.
>
> DTLS is 1:1 with components. You missed the mapping from transports
> to components (1:1..2).
Yes, I was thinking of the DTLS data channel, which is multiplexed onto
a transport.
>
> A candidate pair can only map to a single candidate on each end, so
> your mapping is really just 2 lots of a 1:N mapping (one local, one
> remote).
>
> I'm reluctant to suggest it, but this is really a situation where UML
> really does help.
I'll leave it to you to supply the drawing ....
>
>> Offhand, I don't see a need to have complex stats on any of these, but
>> others might....
> So far, I've seen mention of the fact that this interface will be used
> for more than just statistics. Certificate data might be presented.
Not sure certificate handling is a stats operation. But if it is....
A certificate is a primitive object, isn't it? The moment you start
taking it apart, it loses its certificate-ness, since you can't verify
the signature any more. Can you point to other APIs that handle
certificates in a sensible manner? I don't want to reinvent anything I
can avoid reinventing.
> Though everything could ultimately be mapped down to primitives, that
> can end up suffering a lack of expressiveness, or result in a rough
> user experience.
The first iteration of stats will be a rough user experience, because we
don't know the user yet.
I want to make sure we can get some useful numbers out in the first
iteration, and then iterate.
>
>> On 09/24/2012 06:05 PM, Martin Thomson wrote:
>>> This makes sense to me. Far more so than the flat structure. It
>>> seems clear that the cost of managing structure warrants the
>>> (marginal) extra complexity that this results in.
>>>
>>> Did you consider JSON Pointer as a way to identify nodes in a tree?
>>> Alternatively, you could expose the entire tree in the report without
>>> any need for getValue():
>>>
>>> Your example:
>>> report.getValue("ICE.0")
>>> JSON pointer:
>>> report.getValue("/ICE/0")
>>> DIrect:
>>> report.value.ICE[0]
>>>
>>> These are, after all, just dictionaries and sequences of things. A
>>> direct approach allows for inspection with things like
>>> hasOwnProperty(), the "in" operator, forEach(), and so forth. Much
>>> easier to program to.
>>>
>>> On 24 September 2012 08:13, Eric Rescorla <ekr@rtfm.com> wrote:
>>>> Harald,
>>>>
>>>> In draft-alvestrand-rtcweb-stats-registry-00.txt, you observe that
>>>> there are times when a single named statistics value actually
>>>> corresponds to a number of elements and you would like to be able to
>>>> address them individually. You suggest handling this case with the
>>>> convention of appending a ".X" to the stat in question, but
>>>> I think this actually points to the need towards genuinely
>>>> hierarchical stats.
>>>>
>>>> Consider the case where you want to examine every aspect of ICE,
>>>> which I think there is general consensus we need. At this point
>>>> we have the following containment hierarchy:
>>>>
>>>> - Media Stream [W3C name: track]
>>>> - Component [RTP or RTCP]
>>>> - Local candidate
>>>> - State
>>>> - Check history
>>>> - Estimated RTT
>>>>
>>>> This seems pretty deep to represent cleanly in the existing hierarchy
>>>> but would fit well into a more generic structure.
>>>>
>>>> Here's a strawman to give you an idea of what I have in mind:
>>>>
>>>> - Instead of being just opaque strings, stats identifiers
>>>> should be dot-separated strings, with dots separating
>>>> levels in the hierarchy.
>>>>
>>>> - When registered, each stats identifier must be one of:
>>>>
>>>> * value -- the value is in the stat itself
>>>> * array -- the stat contains a list of values in an array
>>>> (i.e., [])
>>>> * dictionary -- the stat contains a list of values in a
>>>> dictionary (i.e., {})
>>>>
>>>> - You can call getValue() at any level in the hierarchy
>>>> and what you get depends on the identifier type. You
>>>> can subaddress arrays and dictionaries by including
>>>> the index/key in the identifier (as shown below).
>>>>
>>>>
>>>> Reworking your ICE example in this fashion would give us something like
>>>> this:
>>>>
>>>> { local: { timestamp: 12345, stats: {
>>>> SentPackets: 47,
>>>> SentOctets: 4444,
>>>> ReceivedPackets: 33,
>>>> ReceivedOctets: 2346,
>>>> ICE: [
>>>> {
>>>> State: Succeeded
>>>> Used: True,
>>>> LocalIpAddr: '129.241.1.99',
>>>> RemoteIpAddr:'234.978.4.3'
>>>> },
>>>> {
>>>> LocalIPAddr: '10.0.0.1',
>>>> RemoteIPAddr: '10.0.1.24',
>>>> State: Succeeded
>>>> Used: False
>>>> }
>>>> ]
>>>> }}}
>>>>
>>>> ISTM that this places things that naturally go together together,
>>>> and also makes it easier to build processing engines without a lot
>>>> of string manipulation.
>>>>
>>>>
>>>> If I am reading the current API correctly, the only way to actually
>>>> get at a statistics value is to do .getValue() on an RTCStatsReport.
>>>> In this case, the code would then be something like this:
>>>>
>>>> report.getValue('SentPackets') --> 47
>>>> report.getValue('ICE') -->
>>>> ICE: [
>>>> {
>>>> State: Succeeded
>>>> Used: True,
>>>> LocalIpAddr: '129.241.1.99',
>>>> RemoteIpAddr:'234.978.4.3'
>>>> },
>>>> {
>>>> LocalIPAddr: '10.0.0.1',
>>>> RemoteIPAddr: '10.0.1.24',
>>>> State: Succeeded
>>>> Used: False
>>>> }
>>>> ]
>>>>
>>>>
>>>> report.getValue('ICE.0') -->
>>>> {
>>>> State: Succeeded
>>>> Used: True,
>>>> LocalIpAddr: '129.241.1.99',
>>>> RemoteIpAddr:'234.978.4.3'
>>>> }
>>>>
>>>> report.getValue('ICE.0.State') --> 'Succeeded'
>>>>
>>>> Thoughts?
>>>> -Ekr
>>>>
>>
Received on Friday, 28 September 2012 09:55:50 UTC