Violations where Resource Undefined #248
Replies: 2 comments 52 replies
-
If we're missing a span of telemetry, it doesn't mean the resource didn't take on a desired value; it means we don't know what value it took on. It very well may have taken on the desired value, but we can't observe that.
This is my opinion, too. If data is missing, we can say neither that a condition is met nor that it is not met -- we simply don't have the data to make such a determination. Classically, this kind of use-case is what the Example
|
Beta Was this translation helpful? Give feedback.
-
Hi, I'm the annoying guy who keeps changing the conversation to talk about Spans. So what happens when a user converts a So the question is, does it make any sense to include the concept of a null span, so that the conversion is lossless? If so, how would it behave under operations like
Any objections? |
Beta Was this translation helpful? Give feedback.
-
This turns out not to be as simple as our discussion on Slack might have implied.
For context, the ticket that this pertains to is one that resolves AERIE-1825. This ticket involves doing constraints checking on external datasets (which I will also refer to as external resources or external profiles, as that is the language we adopted when working on this ticket). Currently, it is possible to upload external profiles, and this ticket takes the profiles that have been uploaded, integrates them with the constraints DSL, and makes it so that violations are checked on these resources. That is, if I have an external profile “/ext1” uploaded, the constraint
export default () => Constraint: {Real.Resource(“/ext1”).greaterThan(20)}
becomes valid.Generally, this is a pretty straightforward ticket, except for the fact that it is totally possible that these external resources have sparse profile segments. That is, they may not be defined everywhere. If we call
+
a space where the resource is defined and-
a space where it isn’t, then it is totally possible that we could have the following profile:That part isn’t the problem – implementing that hasn’t been much of an issue, and it can address some useful test cases where a resource is based on something like telemetry data, or pertains to a component that isn’t used or attached to the mission for the entirety of a mission, like an alternative power source.
The problem is constraints checking itself. That is, should we flag a constraint as being violated when the resource the constraint operates on isn’t defined? So should the constraint
Real.Resource(“/plant”).equal(3.0)
yield a violation everywhere exceptt=[3,5]
, or only int=[8,9]
?After some deliberation on Slack, where the two conflicting opinions were:
the latter of the two was chosen. That is, when a resource was undefined do not to return a violation. That’s about where slack leaves off. There was discussion of ways to implement this – we want to do this in a way where we don’t really mess with existing implementations of methods like
evaluate()
because the scheduling DSL uses the exact same Java tree nodes, and we don’t want a change in constraints checking to mess with scheduling. This is because currently, the default is a comparison with undefined returns false, but what we imply here is a comparison with undefined should return true. The solution was to isolate all constraint-specific changes the the one constraint-specific class,ViolationsOf.java
, and then do any windowing restrictions using a special method that we add to theExpression.java
class,getWhereDefined()
, which returns an intersection (this will be qualified shortly, it’s not always an intersection) of where the leaf nodes’ resources are defined, and then takes the windows that were found from normal constraints checking and intersects it with the result of that call togetWhereDefined()
. This was a cool solution, except for the fact that it isn’t really well defined for nodes likeForEachActivity
, and there are two cases where it really just doesn’t make a ton of sense, and this has to do with composite constraints:Any
constraint, where one of the subconstraints is defined but false in a given window, and the other subconstraint’s resource is undefined in that same window. In that case, constraints checking evaluates that composite constraint as false, as undefined is treated as false. But should it return a violation? On one hand, none of them are true, so it’s clearly not satisfied, but on the other hand is a constraint isn’t satisfied is the composite constraint completely defined? We leaned heavily towards the latter, but the second case was what really broke this solution.All
constraint, with one subconstraint true in a given window, and the other undefined in the same window. This would evaluate to false, as undefined equals false, and if one is false, all will be. But should we really return a violation? On one hand, technically not every constraint is satisfied, so that’s grounds for violation. But that’s not totally consistent with the idea that an undefined constraint should cause a violation – the constraints that are defined are true, so this shouldn’t be a violation. This is definitely up for debate, but the vagueness here led to Joel proposing another solution, which we also want comment on.The solution was to revert to the initial behavior, i.e. undefined = violation, unless explicitly stated otherwise. That is, we add a new node to the DSL, called
undefined()
, as a method onReal.Resource()
, and it returns the windows for which the resource isn’t defined. Then, by doing the following:Real.Resource("hello").whenDefined().lessThan(3);
(apologies, I can't get multiline code to work here)
you’ve said this is true when the resource is undefined, or where it is defined and it is less than 3, in other words, only evaluate this constraint when this resource is defined.
But, imaginably, users would need to type this often. There could be cases where they might want constraints checking to fail when a resource is undefined (we think?), so this verbosity in some form is important as we would then want users to have the option to not type this (this could be up for debate), but maybe there’s a more concise way. The solution was to add a method to
Real.Resource()
called.whenDefined()
, that translates as follows:Real.Resource("hello").whenDefined().lessThan(3);
.whenDefined()
returnsNullableReal.Resource("hello")
, whereNullableReal.Resource
is the same asReal.Resource
except every time you call something like.lessThan()
or.equals()
, it returns the expanded form above, i.e. it returns:Real.Resource("hello").whenDefined().lessThan(3);
But there might be a cleaner easier way to do this, so comment on that too would be appreciated as well.
After we figure these details out, though, we can finish off AERIE-1825!
Beta Was this translation helpful? Give feedback.
All reactions