eclipse-hono / hono

Eclipse Hono™ Project
https://eclipse.dev/hono
Eclipse Public License 2.0
449 stars 138 forks source link

Reference a group of gateways through which a device can send messages #1072

Closed BobClaerhout closed 4 years ago

BobClaerhout commented 5 years ago

Since https://github.com/eclipse/hono/issues/926 it is possible to configure multiple gateways for a specific device. However, in some cases a group of devices are located on the same spot and can communicate through the same group of gateways. Therefore it would be useful to reference the groupid (of gateways) instead of specifying the same list over and over for each device. By doing this, a change in the group is automatically persisted to all devices. Ultimately we could have group of groups. When certain devices are allowed to roam between different groups of gateways. Currently we have some kind of this:

{
    "device-id": 4711,
    "via" : 
        [
            "gw-1",
            "gw-2",
            "gw-3"
        ]
}
{
    "device-id": 4712,
    "via" : 
        [
            "gw-1",
            "gw-2",
            "gw-3"
        ]
}

This might become:

{
    "device-id": 4711,
    "via" : 
        [
            "gw-group1"
        ]
}

{
    "device-id": 4712,
    "via" : 
        [
            "gw-group1"
        ]
}

{
    "gateway-group-id": "gw-group1",
    "gateways": [
        "gw-1"
    ]
    "gateway-groups": [
        "gw-group2"
    ]
}

{
    "gateway-group-id": "gw-group2",
    "gateways": [
        "gw-2",
        "gw-3"
    ]
    "gateway-groups": [
    ]
}
sophokles73 commented 5 years ago

@BobClaerhout having groups of groups will provide (even) more flexibility, of course. However, how many levels would we need/want to support? Is this immediately relevant to you or would it be fine and in fact an improvement already if we started with just the ability to define and refer to gateway groups in the via property?

BobClaerhout commented 5 years ago

@sophokles73 of course it is already an improvement when we can define a gateway group and refer to it instead of the current list. It might be better to split this up and reconsider the group of groups later on. We have a future use case which might need this functionality. However, it has a lot of other challenges as well since the devices might be roaming over tenants as well...

eriksven commented 4 years ago

In light of #1478 I had some thoughts regarding gateway groups and its realization and identified the following option for the registration and management of such groups within Eclipse Hono:

To summarize the discussion below:

(1) For the group registration, my proposal is to introduce a new, optional, property “isGroup” in the payload for the Device Registry API (https://www.eclipse.org/hono/docs/api/management/#/devices) to indicate a device is a gateway group, i.e. the payload would look like

{
  "enabled": true,
  "defaults": {
    "additionalProp1": {}
  },
  "via": [
    "string"
  ],
  "ext": {
    "additionalProp1": {}
  }
  “isGroup”: false
}

Gateways belonging to that group are listed in the via property. Gateway groups are stored as normal devices. (2) The Device Registration API resolves gateway groups and returns the device ids of the actual gateways instead of group ids.

Detailed discussion

To implement gateway groups, two steps are involved: First, registering gateway groups and, second, processing messages for devices with gateway groups in the via property.

(1.) Registering Gateway groups

To register and manage gateway groups within Eclipse Hono, we identified the following options:

(1.1) Define a new endpoint in the Device Registry Management API (https://www.eclipse.org/hono/docs/api/management/#/devices) The endpoint could be

POST /devices/{tenantId}/groups

and

POST /devices/{tenantId}/groups/{gwGroupId}

I.e. this would be an extension of the already existing the Device Registry Management API.

(1.2) Use the already existing device registration endpoint of the Device Registry Management API and extend the payload in the body. As proposed by @BobClaerhout this could look like

{
    "gateway-group-id": "gw-group2",
    "gateways": [
        "gw-2",
        "gw-3"
    ]
    "gateway-groups": [
    ]
}

This would require the device registry service to recognize that a call to the /devices endpoint is about a gateway group and goes beyond the call for a normal device.

Both options, 1.1 and 1.2, might need an additional data structure in the device registration service to store the group information being separate from the device information.

(1.3) Consider Gateway Groups as a normal device from the point of the view of the /devices endpoint.

In this scenario, a gateway group would be treated as a device. This implies that a gateway group is registered as a device by invoking the endpoint /devices at the device registry API. The gateways that are part of that group are then still listed under the "via" property of this virtual device/gateway. At the same time this would allow the creation of groups of gateway groups. As already discussed in this issue such groups of gateway groups would be a nice to have but is not fully needed at the moment. However, this approach might have performance implications if the gateway structures become too complex, deep or contain loops that need to be detected.

(1.4) Use a combination of 2. and 3.

Here a gateway group stays a normal device but we add the optional property isGroup which is a boolean with default value set to false. If isGroup is set to true this is an explicit indication that this device is not an actual device but a group definition for the devices listed in the "via" property. As mentioned for 1.3, this introduces the option (or danger) of introducing complex structures for the gateway hierarchy. However, the explicit indication that this device is a group makes it easier to directly distinguish it from a normal device compared to 1.3.

What are the advantages of the different approaches? One advantage of having a dedicated data structure for the gateway groups as in 1.1 and 1.2 compared to handling groups as devices as in 1.3 and 1.4 is that it is easier to perform operations on group level (like get all groups) because one does not need to search all devices to find the gateway groups. As of now I do not see many usages for such operations.

From the described options, my proposal is still to follow approach 1.4 because it does only require smaller changes to the data model. In addition, the device registry shall only support groups that do not contain sub groups. This feature could be checked during the creation and update of groups by checking that all members of the current group do not have the isGroup property enabled.

(2) Processing Messages

The second stage that is affected by the Gateway group concept is the processing of messages. One API that would be heavily affected by the concept of gateway groups is the assert device registration endpoint of the Device Registration API (https://www.eclipse.org/hono/docs/api/device-registration/#assert-device-registration). In case the Device Registration API is invoked, the device registry service needs to process the gateway_id if provided. Without gateway groups it is sufficient to check the via property of the device with the id device_id for the presence of the gateway_id. With gateway groups we need to resolve the device ids of the members of the gateway group to check whether the gateway_id in question is a member of one the groups registered for the device.

In option 1.1 and 1.2, the members of the group could be retrieved from the gateway group data structure that would be added in the device registry service. For approach 1.3 and 1.4, the device registry would need to flatten the gateway structure for the device with the id device-id. So the device registry service would create a list and add recursively all ids that are mentioned in the via property of the devices in that list. To avoid issues such as circles or too complex hierarchies one could limit the depth of the search to one level.

The payload of the response message of the assert device registration may also contain a list with ids of the registered gateways for the device with the id device_id in a via property. Without gateway groups the device registry service could simply return the list under thevia property of the device which it needs to retrieve anyway for the assertion. But if that list contains gateway groups the device registry service may have the choice to either directly return the id of a gateway group or to retrieve all members of that gateway group which it might have to do anyway for the assertion too. My proposal is to keep the concept of gateway groups and the actual ids in the scope of the device registry service and make the translation between a gateway group and its members transparent to the other services. So the response message would only contain the ids of the actual gateways and no group ids.

What do you think? Which consumers of the Device Registration API would be more interested in the gateway group instead of its members and the actual gateways?

sophokles73 commented 4 years ago

@eriksven Thanks a lot for the detailed overview of your idea(s) regarding this issue. Here are some thoughts of mine:

My proposal is to keep the concept of gateway groups and the actual ids in the scope of the device registry service and make the translation between a gateway group and its members transparent to the other services. So the response message would only contain the ids of the actual gateways and no group ids.

I agree. Clients of the Device Registration API should not need to de-reference gateway groups but instead should get back a list of gateway IDs in the response to assert Registration.

From the described options, my proposal is still to follow approach 1.4 because it does only require smaller changes to the data model.

I see the point in trying to make as little modifications as possible. However, I do not really like the abuse of the device registration structure for the purpose of gateway groups. A gateway group seems to be a different concept and as such should be represented as such IMHO. I also think that modeling the relation between a group and its members using the via property is very confusing as it kind of inverts the direction of its part-of semantics and as such could lead to quite a long list that needs to be embedded in a single group object (if there are many gateways being part of that group). Based on that, I would actually prefer to have separate resources for managing gateway groups and indicate a gateway's membership in a group by means of a properly named property of the device object, e.g. member-of, to make the concept more explicit and easier to understand (and maintain). In a first step, resolution of member relationships might be restricted to one level only, but this could be extended in the future to allow for groups of groups.

@BobClaerhout @calohmn WDYT?

BobClaerhout commented 4 years ago

@eriksven You've given it a proper thought, nice! Thanks for this extended overview.

I agree. Clients of the Device Registration API should not need to de-reference gateway groups but instead should get back a list of gateway IDs in the response to assert Registration.

I agree with this as well. When requesting the via property of a specific device it should keep returning the list of gateways (whether they are in a group or not).

I see the point in trying to make as little modifications as possible. However, I do not really like the abuse of the device registration structure for the purpose of gateway groups. A gateway group seems to be a different concept and as such should be represented as such IMHO. I also think that modeling the relation between a group and its members using the via property is very confusing as it kind of inverts the direction of its part-of semantics and as such could lead to quite a long list that needs to be embedded in a single group object (if there are many gateways being part of that group). Based on that, I would actually prefer to have separate resources for managing gateway groups and indicate a gateway's membership in a group by means of a properly named property of the device object, e.g. member-of, to make the concept more explicit and easier to understand (and maintain). In a first step, resolution of member relationships might be restricted to one level only, but this could be extended in the future to allow for groups of groups.

I also agree with this. Misusing the via property results indeed in less modifications but will be very confusing and error prone whenever this will be refactored in the future. I'm in favor of a separate entity. I would therefore go with option 1.1

Additionally, I think we need to be realistic about the numbers on these groups. E.g.: https://www.thethingsnetwork.org/, they have 9919 (today) gateways active in the complete network. If this network would be a group (or even a combination of groups), the load on the server (which will receive millions of devices on these gateways all together) will be quite low compared to the load of the devices. although this depends on how you use the gateway of course.

eriksven commented 4 years ago

Thank you @sophokles73 and @BobClaerhout for your comments. Indeed my goal was to make the changes as minimal inversive as possible. But separating the concept of gateway groups and devices sounds better to me too. So I tried to come up with an proposals of how such group resources and possible endpoints could look like and thus followed more or less approach 1.1. The results are in the end of this comment.

The storage of the membership information would be handled within the device resource by adding a memberOf property as raised by @sophokles73 . This ensures, that the membership information is only stored in one place and that every member of the group is a valid device. So when a device is deleted, it is then automatically removed from the group without the need to implement further logic for the synchronisation between the device and the group resources.

As of now the only property that would be stored with the group resource itself would be the group id and with that the information that the group actually exists. However, by adding a group id to the memberOf property group is more or less created implicitly. Of course, having a more explicit second structure makes it easier to use and understand the group concept if it becomes more complex. But since the group resource would only contain one id, I currently see no added value by providing dedicated endpoints for a group resource or a separate data structure for groups.
Hence, my approach would now in the direction of 1.2 to "only" add a memberOf property to the device resource. In a later stage when we have more properties for the group to store, we could still add an endpoint for the groups.

WDYT?

Proposal for endpoints: - Creating new groups ``` POST /groups/{tenantId}/{groupId} ``` and ``` POST /groups/{tenantId}/ ``` in case of success both endpoints return the id of the new group - check for existince of group (maybe retrieve more details later) ``` GET /groups/{tenantId}/{groupId} ``` if the group exists, it returns the id of the group. - To add a gateway to a group, the gateway device will have a new parameter "memberOf" in the body. Ie the device endpoint of the device registry management service is changed. The the body of the resources ``` POST /devices/{tenantId} ``` or ``` POST /devices/{tenantId}/{deviceId} ``` becomes ``` { "enabled": true, "defaults": { "additionalProp1": {} }, "via": [ ], "memberOf": [ "group1", "group2" ], "ext": { "additionalProp1": {} } } ``` Remark: If we do support groups of gateway groups, it must be checked on a semantic level that ``memberOf`` is never set if the via property is set too. Otherwise devices using other gateways could be added to gateway group too. Delete group - Gateways are removed from a group by changing the ``memberOf`` parameter of the gateway ``` PUT /devices/{tenantId}/{deviceId} { "enabled": true, "defaults": { "additionalProp1": {} }, "via": [ ], "memberOf": [ "group1" ] "ext": { "additionalProp1": {} } } ```
calohmn commented 4 years ago

@eriksven First of all thanks for the detailed presentation of the different options above. I also agree with keeping the translation of the gateway groups in the device registration service.

As to the data model: I think using a memberOf property to define the group membership is a good way to go.

However, by adding a group id to the memberOf property group is more or less created implicitly. [...] Hence, my approach would now in the direction of 1.2 to "only" add a memberOf property to the device resource.

I see the charm of it, not having to define a new top level groups resource in the device management API. On the other hand such a resource would be good to have if the device registry implementation wants to validate the insert/update of device entries having memberOf and via properties (ensuring referential integrity). So, maybe we could make the groups resource optional for now.

eriksven commented 4 years ago

Created a PR for adding the 'memberOf' property in #1670

sophokles73 commented 4 years ago

@BobClaerhout can you check if this can be closed with #1683 ?

BobClaerhout commented 4 years ago

@BobClaerhout can you check if this can be closed with #1683 ?

Had a quick review and seems ok. Just had one remark about the documentation.

sophokles73 commented 4 years ago

@BobClaerhout

Just had one remark about the documentation.

Which is? where can I find it? Does it prevent this issue from being closed? If not, can you close it?

BobClaerhout commented 4 years ago

@BobClaerhout

Just had one remark about the documentation.

Which is? where can I find it? Does it prevent this issue from being closed? If not, can you close it?

On the PR. I think we close this. It's just a details.