open-coap / java-coap

CoAP Java library
Apache License 2.0
18 stars 5 forks source link

Shared 'observe relation' store at CoAP client side. #36

Closed sbernard31 closed 9 months ago

sbernard31 commented 1 year ago

Following https://github.com/open-coap/java-coap/issues/27, I created this issue.

Short Summary :

Being able to store observe relation at CoAP client side in a shared store. The idea is to not lost observation relation on reboot but also being able to share observe relation in a cluster.

Long story :

A LWM2M server in production often need to persist observe relation or to shared when deployed in a cluster. Note that from an CoAP observe perspective, a LWM2M server act as a CoAP client.

About persistency : When you create a LWM2M server based on Leshan and you manage lot of clients in production with lot of Observe Relation. When you reboot your server, this will be an issue to resend all observe requests.

About cluster : Most of people who use Leshan in a production server want to be able to launch it in a cluster and so observation relation must be shared between all instance of the cluster. Currently in Californium this is achieve with a ObservationStore interface.

This issue aims to discuss about :

Some challenge we need to have in mind are probably described in leshan wiki.

Some design questions :

szysas commented 1 year ago

We could start discussing this issue.

I have a proposal such as:

To have a single observation listener for all subscriptions such as:

public interface ObservationListener {
   // return false to will terminate observation 
   boolean onObservation(String resourceUriPath, CoapResponse observation);
}

And observation store interface, that could be implemented as database:

public interface ObservationRelationsStore {
    void add(CoapRequest obsReq);
    String getUriPathOrNull(SeparateResponse obs);
    void remove(SeparateResponse obs);
sbernard31 commented 1 year ago

It's hard to me to see how this will be used as I didn't know java-coap so well for now.

But I can try to provide some feedback. (keep in mind that I maybe don't really get what you have in mind)

  1. About ObservationListener : not sure resourceUriPath is enough to identify a observation ? I feel that we need the token (maybe it is available in CoapResponse ?) and probably a kind of peer identifier (maybe it is available in CoapResponse transport context ?) OR CoapResponse should be replace bySeparatedResponse` ?

  2. About add(CoapRequest obsReq), to store all information I need, I will probably need to attach some userData to CoapRequest? Maybe I will be able to do that using transport context ?

  3. About getUriPathOrNull(SeparateResponse obs), remove(SeparateResponse obs), I guess I should be able to find observe relation from identifier based token of Separated Response + peer Identity from SeparatedResponse transport context.

szysas commented 1 year ago
  1. About ObservationListener : not sure resourceUriPath is enough to identify a observation ? I feel that we need the token (maybe it is available in CoapResponse ?) and probably a kind of peer identifier (maybe it is available in CoapResponse transport context ?) OR CoapResponse should be replace bySeparatedResponse` ?

Good point, it should be SeparatedResponse

public interface ObservationListener {
   // return false to will terminate observation 
   boolean onObservation(String resourceUriPath, SeparatedResponse observation);
}
  1. About add(CoapRequest obsReq), to store all information I need, I will probably need to attach some userData to CoapRequest? Maybe I will be able to do that using transport context ?

Well, that will be called by server when handling new subscription (GET with obs header). What kind of user data are you thinking of? Would it be possible to retrieve such meta data by implementation class of ObservationRelationsStore?

  1. About getUriPathOrNull(SeparateResponse obs), remove(SeparateResponse obs), I guess I should be able to find observe relation from identifier based token of Separated Response + peer Identity from SeparatedResponse transport context.

Yes, all those info will be available.

sbernard31 commented 1 year ago

Well, that will be called by server when handling new subscription (GET with obs header). What kind of user data are you thinking of? Would it be possible to retrieve such meta data by implementation class of ObservationRelationsStore?

I will probably create a kind of adapter which will implement ObservationRelationsStore interface with an internal reference to Leshan RegistrationStore.

When void add(CoapRequest obsReq); will be called, internally I will call :

https://github.com/eclipse/leshan/blob/33cbe86744774f3a81f215606425a4b3f5d12168/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationStore.java#L100-L109

But I need the registrationId (In LWM2M the observe relation is tied to Registration concept). When I send the ObserveRequest, I know I send it to the device with the given registration so when the relation is added to the store I must tied it to that registration.

That's why I would like to add the registration id to the coap request when I translate the LWM2M request to CoAP request, so I can find it back on ObservationRelationsStore.add

I hope I'm almost clear :grin:

szysas commented 1 year ago

That's why I would like to add the registration id to the coap request when I translate the LWM2M request to CoAP request, so I can find it back on ObservationRelationsStore.add

I hope I'm almost clear

Adding registration-id (or any other meta data) is possible through TransportContext so I don't see issue there.

Yes, that was very clear.

szysas commented 1 year ago

Minor updates to interfaces:

public interface ObservationRelationsStore {
    void add(CoapRequest obsReq);       // contains: Uri-path, Token, IP address, and TransportContext
    void remove(SeparateResponse obs);  // contains: Token, IP address and TransportContext
    Optional<String> resolveUriPath(SeparateResponse obs); // contains: Token, IP address and TransportContext
public interface ObservationListener {
    /**
     * Handles incoming observation
     * @return if false then server will terminate observation by sending RESET
     */
    boolean onObservation(String resourceUriPath, SeparateResponse observation);