Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add design document discussing QoS reconfigurability #300

Merged
merged 30 commits into from
Nov 17, 2020

Conversation

ivanpauno
Copy link
Member

Alternative to #296.
Based on feedback, the proposal leverages ROS parameters instead of proposing an ad-hoc file format.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno added the enhancement New feature or request label Sep 23, 2020
@ivanpauno ivanpauno self-assigned this Sep 23, 2020
@ivanpauno
Copy link
Member Author

@jacobperron this is ready for a first pass.

Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I think the motivation section really helps focus the article.

I've left some wording suggestions and other opinions.

articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
ivanpauno and others added 16 commits September 30, 2020 13:20
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno marked this pull request as ready for review October 5, 2020 14:58
articles/qos_configurability.md Outdated Show resolved Hide resolved
ros__parameters:
qos_profiles: # group for all the qos settings
publisher:
'my/fully/qualified/topic/name/here':

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the topic name is sufficient. It seems to me that topic+message_type would properly discriminate. In other words, not all message types using the same topic name should be expected to use the same QoS settings.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the most common use cases, each topic has only one message type.

Always including the topic type in the parameter sounds a bit verbose to me, and the "publisher/subscription id" trick mentioned later should cover this use case.
Anyways, including the topic type in the parameter name sounds like an acceptable proposal to me (though it will get problematic to make the parameters URI unique for a given fq node name, entity type, fq topic name and topic type).

Copy link

@mjeronimo mjeronimo Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it's true that each topic typically has only one associated message type, I don't think we should build in this assumption since, in general, ROS2 allows for multiple types per topic. Regardless of particular solution, there should be a way to differentiate between TopicA/MessageTypeA and TopicA/MessageTypeB when specifying QoS settings.

I'm wondering about using the ID approach (when creating a publisher)... I suppose this change would propagate to other functions, such as get_publishers_info_by_topic, where you'd need to augment the TopicEndpointInfo class to have a method to get this ID.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about using the ID approach (when creating a publisher)... I suppose this change would propagate to other functions, such as get_publishers_info_by_topic

I don't think that propagating the concept to graph functions is a good idea.

Having an ad-hoc id only to be able to distinguish publishers/subscribers when re-configuring QoS might not be a great idea either.
If we think that distinguishing publishers in the same topic within a node is not necessary (see this comment), the concept of "id" can be deleted and the topic type can be added to the parameter name.

It's a bit problematic to add the topic type to the parameter URI though, e.g.:

  ros__parameters:
    qos_profiles:  # group for all the qos settings
      publisher:
        'my/fully/qualified/topic/name/here':
          'std_msgs.msg.String':

current addressing scheme:

ros_param://my/full/namespace/ny_node_name/qos_profiles.publisher.my/fully/qualified/topic/name/here.std_msgs.msg.String

alternative addressing scheme

ros_param://my.full.namespace.ny_node_name/qos_profiles/publisher/my/fully/qualified/topic/name/here.std_msgs.msg.String

well, that actually seems to work.


That will make the parameter names much more verbose, but I think that's ok.
The type cannot be optional, because that would imply declaring two parameters (except if we want to not use parameters for this, like in #296).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is more verbose, but I think it's consistent with the general idea of allowing for multiple types per topic name.

Regarding this:

If we think that distinguishing publishers in the same topic within a node is not necessary...

I would recommend starting with an addressing scheme that includes the type. Then, see if someone comes up with the requirement to distinguish between multiple publishers with the same topic and type in a single node. I'd like to hear what others have to say about this, though.

This comment was marked as resolved.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gbiggs One example is the ROS2 node's get_topic_names_and_types method, which returns a map of strings to vectors of strings:

  /// Return a map of existing topic names to list of topic types.
  /**
   * \sa rclcpp::Node::get_topic_names_and_types
   */
  RCLCPP_LIFECYCLE_PUBLIC
  std::map<std::string, std::vector<std::string>>
  get_topic_names_and_types(bool no_demangle = false) const;

Implying that each topic name can be associated with multiple types.

I don't want to get off-topic, but if this is indeed the case I think this also will break any tools that make the assumption of one topic name per one topic type. For example, rqt_bag, which I've been working on, has a row on the message timeline for each topic name. To properly support 1 topic name/n topic types, rqt_bag would need one row per combination.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal opinion after all of this is that if you use more than one type per topic, and you want to configure these, then you should use a named entity (so called "id").


I think it's somewhat unlikely that within a single node there are two entities with the same topic name but separate types. I would consider this a bug and discourage it. It is more likely, in my opinion, that two entities in different nodes have the same name but different types, even though I'd still discourage that. But in that situation you could still configure them separately as they are namespaced by the node name and namespace.

It is true that we allow for more than one type per topic, but that's not because we encourage it, but because of the nature of distributed discovery, since there might be remote entities with the same name but different types. That's the main reason for the discovery API allowing for that.

In fact, unless it changed, most DDS vendors will not allow you to create a topic with the same name but different types within a single participant. It only comes up when the conflicting entity is remote or at least in a different participant. So their behavior is a bit asymmetric on that point.

I think adding the type to the name of the parameter could work, but I'd prefer keeping the common case simple (each topic has a different type) and letting users use the named entity mechanism if they need to do the more complicated thing (entities share a topic name but not a type).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal opinion after all of this is that if you use more than one type per topic, and you want to configure these, then you should use a named entity (so called "id").

My last question about this: Do we want the name of the entity ("id") to be complementary to the topic name (like described in the current proposal) or to be a replacement for it (i.e. the default "id" is the topic name and that can be overridden)?

More details in this comment.

I don't have a strong preference about either option, using the "id" as an optional complement to the topic name is more restrictive, but that can be seen as an advantage (less ways of people getting creative on how to provide qos profile overrides).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any opinion on id vs <entity>_id.

articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
…ime' when appropriate

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

Another alternative to solve the discussions here, here and here, I was thinking that having a "profile id" that can be independent of the topic name and type may be a good idea.

i.e.: the user provides a construction time an id

node->create_publisher(
  topic_name, qos,
  QosOverridingOptions::Builder()
    .id("my_profile_id")
    .policies({QoSPolicy::Reliability, QoSPolicy::History})
    .callback(callback)
    .build());

and then the file looks like:

/my/node/name:
  ros__parameters:
    qos_profiles:
      publisher:
        my_profile_id:
          reliability: reliable
          history: keep_last

In that way, the node's author can use the same profile name in several entities that must share the qos profile.
I would use as the default id the topic name (after expansion and remapping), but allowing the node author's to change it covers all possible use cases without the need of adding more levels of nesting (the type and an optional id).

The profile id could be remappable (using the same remapping rules that services and topic names) or not, I think it doesn't matter because all parameters are "private" to a node.
Caveat: topic names can be remapped, so if publisherA uses id1, and publisherB uses its topic name as the id and it gets remapped to id1, then you have a problem. But that can easily be avoided by the user doing systems integration.

articles/qos_configurability.md Show resolved Hide resolved
ros__parameters:
qos_profiles: # group for all the qos settings
publisher:
'my/fully/qualified/topic/name/here':

This comment was marked as resolved.

ros__parameters:
qos_profiles: # group for all the qos settings
publisher:
'my/fully/qualified/topic/name/here':

This comment was marked as resolved.

Up to ROS 2 Foxy, read-only parameters generate a parameter event with its initial value when declared.
If we start declaring parameters for each QoS policies of many entities, the amount of parameter events at startup time will significantly increase, as the declaration of each parameter generates an event.

To avoid this issue, a way to opt-out parameter events when declaring a parameter could be added.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general we probably want to opt-out of infrastructure parameter events by default, not just the QoS ones.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's reasonable.

Do you mean?

  • Turn off parameter events for read-only parameters.
  • Turn off by default parameter events generated when declaring a parameter, but continue generating events when a parameter is set.
  • Turn off by default all parameter events.

rclcpp currently depends on parameters events to make use_sim_time works, but that's fixable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean to turn off parameter events for any parameters used internally by the ROS infrastructure, in the way these QoS ones are. I don't know how many of those there are at this time, though.

My reasoning is that a node author is going to care about events for parameters they declare themselves, but will find any additional events that they have to handle in some way to be noise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense.
I miss-read your previous comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think we shouldn't do this. I think we should treat all of the parameters the same. It would be confusing to me as a user to see that some parameters cause events and others do not.

Answering the question "which parameters generate events" is not very trivial then.

That being said, if we do this, then it should be possible for the user to have parameters that do not generate events as well, i.e. we should dog food this feature.

articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved

#### Only declaring some parameters

Instead of hidden parameters that correspond to the QoS policies, we could only declare parameters for the policies that the user want to be reconfigurable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the earlier approach combined with hidden parameters is more usable. As a bonus, hidden parameters will be useful in many other places in the ROS infrastructure.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Geoffrey Biggs <gbiggs@killbots.net>
@jacobperron
Copy link
Member

Using a profile ID as an alternative seems viable and perhaps simpler. Instead of defaulting the ID to the topic name, I would rather make the profile ID a hard requirement if an author wants to make their publisher or subscription QoS configurable. For example:

node->create_publisher(
  topic_name, qos,
  QosOverridingOptions::Builder("my_profile_id")
    .policies({QoSPolicy::Reliability, QoSPolicy::History})
    .callback(callback)
    .build());

This avoids any issues with topic remapping.

We'll probably want tooling to be able to query what pub/subs are associated with what profile IDs (post-remapping).

@mjeronimo
Copy link

I agree with @jacobperron that using a profile ID but not defaulting to the topic name would a better way to go. It seems pretty straightforward and less error prone (remapping a topic name resulting in an expected change of QoS settings).

@gbiggs
Copy link
Member

gbiggs commented Oct 8, 2020

If the ID is specified in source code, then it would need to be scoped to the node. Otherwise we could have two nodes from different authors using the same ID and clashing.

@ivanpauno
Copy link
Member Author

If the ID is specified in source code, then it would need to be scoped to the node. Otherwise we could have two nodes from different authors using the same ID and clashing.

Parameter names are all "private" to a node (and not global, like in ROS 1), so that's not an issue.


@jacobperron @mjeronimo My main motivation to use the "resolved" topic name as the default id is that it makes pretty easy to set the QoS when you want is to set the same profile for all nodes for all entities in the same topic:

/**:
  ros__parameters:
    qos_profiles:
      publisher:
        '/my/topic/name':
          reliability: reliable
          history_depth: best_effort

If desired, the qos parameters can be overridden for an specific node.
In the weird case that a node wants different qos parameters for the same topic, the you can override the id with something else.

But using the "resolved" topic name as the profile id makes configuration easy for the typical use case (and not impossible for strange use cases).

e.g.: node A publishes to /my_topic, node B publisher to /another_topic.
Both are remapped to /new_topic.
If the profile id was also modified to /new_topic, then you can configure the same qos easily using node wildcards (like the example above).

@jacobperron
Copy link
Member

Defaulting to the topic name sounds okay too. The re-usability argument is convincing.
In any case, I think using the (optional) pub/sub ID as a QoS profile ID makes sense.

articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
ros__parameters:
qos_profiles: # group for all the qos settings
publisher:
'my/fully/qualified/topic/name/here':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal opinion after all of this is that if you use more than one type per topic, and you want to configure these, then you should use a named entity (so called "id").


I think it's somewhat unlikely that within a single node there are two entities with the same topic name but separate types. I would consider this a bug and discourage it. It is more likely, in my opinion, that two entities in different nodes have the same name but different types, even though I'd still discourage that. But in that situation you could still configure them separately as they are namespaced by the node name and namespace.

It is true that we allow for more than one type per topic, but that's not because we encourage it, but because of the nature of distributed discovery, since there might be remote entities with the same name but different types. That's the main reason for the discovery API allowing for that.

In fact, unless it changed, most DDS vendors will not allow you to create a topic with the same name but different types within a single participant. It only comes up when the conflicting entity is remote or at least in a different participant. So their behavior is a bit asymmetric on that point.

I think adding the type to the name of the parameter could work, but I'd prefer keeping the common case simple (each topic has a different type) and letting users use the named entity mechanism if they need to do the more complicated thing (entities share a topic name but not a type).

articles/qos_configurability.md Show resolved Hide resolved
Up to ROS 2 Foxy, read-only parameters generate a parameter event with its initial value when declared.
If we start declaring parameters for each QoS policies of many entities, the amount of parameter events at startup time will significantly increase, as the declaration of each parameter generates an event.

To avoid this issue, a way to opt-out parameter events when declaring a parameter could be added.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think we shouldn't do this. I think we should treat all of the parameters the same. It would be confusing to me as a user to see that some parameters cause events and others do not.

Answering the question "which parameters generate events" is not very trivial then.

That being said, if we do this, then it should be possible for the user to have parameters that do not generate events as well, i.e. we should dog food this feature.

node->declare_parameters_atomically(...); // names and defaults for all parameters given here
// callback_handle goes out of scope, the callback is auto-removed.
```
`declare_parameters_atomically` would be needed, if not you cannot have conditions like `history == keep_all or history_depth > 10`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should at least set the QoS parameters for a single topic atomically, but honestly, maybe all of them should be set atomically. What if you wanted publisherA.history == subscriptionB.history?

So I agree we need a declare_parameters_atomically().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you wanted publisherA.history == subscriptionB.history?

That's a good question, but I think in that case the user can control in which order they create entities:

publisher = node->create_publisher(
  "my_topic",
  my_pub_default_qos,
  [] (const QoSProfile & qos) -> bool {
      return qos_profile_is_valid(qos);
  });

subscription = node->create_subscription(
  "my_topic",
  my_sub_default_qos,
  subscription_callback
  [required_history=publisher->get_qos().get_history()] (const QoSProfile & qos) -> bool {
      return qos.get_history() == required_history && qos_profile_is_valid_2(qos);
  });

Declaring all qos parameters atomically sounds like a valid alternative, but I'm not sure when that should happen.

articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
articles/qos_configurability.md Outdated Show resolved Hide resolved
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

FYI (to everyone in the thread), I have opened some PRs implementing the proposal:

Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document LGTM. I think most (or all?) comments have been addressed. I think we can merge this (maybe with a second approval, perhaps@mjeronimo). We can always make future amendments and additions where necessary.

articles/qos_configurability.md Outdated Show resolved Hide resolved
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

Merging, thanks @gbiggs @jacobperron @mjeronimo @wjwwood @dejanpan for the feedback in #296 and here.

If there's any extra comments I can address them in a follow-up.

@ivanpauno ivanpauno merged commit 2dbb691 into gh-pages Nov 17, 2020
@delete-merged-branch delete-merged-branch bot deleted the ivanpauno/qos-external-configuration branch November 17, 2020 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants