Lorawan-stack: Duplicate correlation IDs

Created on 10 May 2019  ·  8Comments  ·  Source: TheThingsNetwork/lorawan-stack

Summary

The correlation IDs in some events (i.e. gs.up.receive and as.up.receive) are duplicate.

Steps to Reproduce

  1. Subscribe to events, i.e. ttn-lw-cli events subscribe --gateway-id ...

What do you see now?

For example:

{
  "name": "gs.up.receive",
  "time": "2019-05-10T08:48:36.397803Z",
  "identifiers": [
    {
      "gateway_ids": {
        "gateway_id": "tektelic-micro",
        "eui": "647FDAFFFE0059AB"
      }
    }
  ],
  "data": {
    "@type": "type.googleapis.com/ttn.lorawan.v3.UplinkMessage",
    "raw_payload": "QKogASYAkdgBU2NToyoatD/C",
    "settings": {
      "data_rate": {
        "lora": {
          "bandwidth": 125000,
          "spreading_factor": 7
        }
      },
      "coding_rate": "4/5",
      "frequency": "867900000",
      "timestamp": 3375161699
    },
    "rx_metadata": [
      {
        "gateway_ids": {
          "gateway_id": "tektelic-micro",
          "eui": "647FDAFFFE0059AB"
        },
        "timestamp": 3375161699,
        "rssi": 2,
        "snr": 10,
        "uplink_token": "ChwKGgoOdGVrdGVsaWMtbWljcm8SCGR/2v/+AFmrEOPCs8kM"
      }
    ],
    "received_at": "2019-05-10T08:48:36.397329Z",
    "correlation_ids": [
      "gs:conn:01DAGEFV4TBH5K9AVAX86NPDXM",
      "gs:uplink:01DAGEW31DQZQNSCQPJ28KGFMF"
    ],
    "gateway_channel_index": 4
  },
  "correlation_ids": [
    "gs:conn:01DAGEFV4TBH5K9AVAX86NPDXM",
    "gs:uplink:01DAGEW31DQZQNSCQPJ28KGFMF",
    "gs:conn:01DAGEFV4TBH5K9AVAX86NPDXM",
    "gs:uplink:01DAGEW31DQZQNSCQPJ28KGFMF"
  ],
  "origin": "Johans-MacBook-Pro.local"
}

What do you want to see instead?

No duplicates in correlation_ids

How do you propose to implement this?

The correlation IDs probably get added twice. This may be in the events package where correlation IDs are added when they are in the context.

Can you do this yourself and submit a Pull Request?

Yes

bug shared

Most helpful comment

I would prefer using @rvolosatovs's merge algorithm (see my comment). We already spent the time on making an efficient implementation for merging correlation IDs, so it would be a shame if we'd merge/dedup with @pgalic96's proposed map implementation that is a lot less efficient and also returns unsorted IDs.

All 8 comments

@htdvisser the problem seems that the correlation IDs from the event payload are appended to those of the context, i.e. if you have this;

ctx := events.ContextWithCorrelationID(ctx, fmt.Sprintf("gs:uplink:%s", events.NewCorrelationID()))
msg.CorrelationIDs = append(msg.CorrelationIDs, events.CorrelationIDsFromContext(ctx)...)
registerReceiveUplink(ctx, conn.Gateway(), msg)

The correlation IDs are duplicate in the event as they are both in ctx and in msg (through GetCorrelationIDs()).

Should we check uniqueness here?

https://github.com/TheThingsNetwork/lorawan-stack/blob/master/pkg/events/events.go#L148

Yes, we can move the merge algorithm in pkg/events/correlation_context.go into its own func (mergeCorrelationIDs) and use that in pkg/events/events.go:

    if cids := data.GetCorrelationIDs(); len(cids) > 0 {
        cids = append(cids[:0:0], cids...)
        sort.Strings(cids)
        evt.innerEvent.CorrelationIDs = mergeCorrelationIDs(evt.innerEvent.CorrelationIDs, cids)
    }

We should probably add a note that evt.innerEvent.CorrelationIDs must be sorted (which it is, because correlation IDs in the context are sorted).

What is the status @pgalic96 ?

got busy with stuff, I made the check for uniqueness, something like this:

if data, ok := data.(interface{ GetCorrelationIDs() []string }); ok {
        mapCorrelationIds := make(map[string]struct{})
        for _, correlationID := range evt.innerEvent.CorrelationIDs {
            mapCorrelationIds[correlationID] = struct{}{}
        }
        for _, correlationID := range data.GetCorrelationIDs() {
            if _, ok := mapCorrelationIds[correlationID]; !ok {
                evt.innerEvent.CorrelationIDs = append(evt.innerEvent.CorrelationIDs, correlationID)
            }
        }
    }

haven't written a test yet, but it would be creating an event with TestCorrelationID and then supplying the same TestCorrelationID in the payload, and then checking that the length of the CorrelationIDs of the message is indeed 1 and not 2.

Apart from the variable name it generally looks good, although the evt.innerEvent.CorrelationIDs don't get deduplicated this way

I would prefer using @rvolosatovs's merge algorithm (see my comment). We already spent the time on making an efficient implementation for merging correlation IDs, so it would be a shame if we'd merge/dedup with @pgalic96's proposed map implementation that is a lot less efficient and also returns unsorted IDs.

@pgalic96 can you give a status update?

I haven't been working on it this week yet, coming to office tomorrow after exam. will try to finalize it tomorrow.

I will change the code according to @htdvisser suggestion.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adriansmares picture adriansmares  ·  9Comments

johanstokking picture johanstokking  ·  8Comments

ecities picture ecities  ·  5Comments

kschiffer picture kschiffer  ·  6Comments

ecities picture ecities  ·  5Comments