# TagRetirementList vs. BasicScanclassHistorySet

**URL:** <https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566>\
**Category:** Module Development\
**Created:** [December 13, 2022, 6:18pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566 "2022-12-13T18:18:58Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![nicholas.robinson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.robinson/32/22188_2.png) [@nicholas.robinson](https://forum.inductiveautomation.com/u/nicholas.robinson)\
**Post date:** [December 13, 2022, 6:18pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/1 "2022-12-13T18:18:58Z")

</div>

In our kafka tag history sink we send tag history records to kafka like this:

```auto
    @Override
    public void storeData(HistoricalData data) throws IOException {
        int pathIndex = 0;

        for (HistoricalData row : BasicDataTransaction.class.cast(data).getData()) {
            BasicScanclassHistorySet scanset = BasicScanclassHistorySet.class.cast(row); // line 40
            if (scanset.size() == 0) continue;

            String gatewayName = this.hostName;
            String provider = scanset.getProviderName();
            pathIndex = provider.length() + 2;

                for (HistoricalTagValue tagValue : scanset) {
                    try{
                        String json = new JSONObject()
                                .put("gatewayName", gatewayName)
                                .put("provider", provider)
                                .put("tagPath", tagValue.getSource().toString().replace("["+provider+"]", ""))
                                .put("type", tagValue.getTypeClass())
                                .put("quality", tagValue.getQuality())
                                .put("value", String.valueOf(tagValue.getValue()))
                                .put("epochms", tagValue.getTimestamp().getTime())
                                .toString();

                        SinkData value = new SinkData(topic, json, this.getPipelineName());
                        this.sendDataWithProducer(value);

                    } catch (JSONException e) {
                        logger.error("Error sending tag: " + e.toString());
                    }
                }
        }
        if (pathIndex > 0) {
            setLastMessageTime(this.name);
        }
    }

```

In that tag history stream though, we sometimes see the following:

> com.inductiveautomation.ignition.gateway.history.TagRetirementList

These result in errors and store and forward quarantined records:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/4/34427319492e3b3249c90ce69328342c99f07520.png)

I am thinking to add an if statement or try/catch that checks if the record is a tag retirement and if so don't send it to the sink, do nothing in stead (maybe just send a message to the log).

What would be the best way to check if the row is a tag retirement?

Thanks,

Nick

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [December 13, 2022, 6:20pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/2 "2022-12-13T18:20:16Z")

</div>

`instanceof`? I don't think there's any real API for it. You'll just have to check the type at runtime before making assertions with a cast.

---

<div class="post-metadata">

**Author:** ![nicholas.robinson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.robinson/32/22188_2.png) [@nicholas.robinson](https://forum.inductiveautomation.com/u/nicholas.robinson)\
**Post date:** [December 13, 2022, 6:25pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/3 "2022-12-13T18:25:24Z")

</div>

10-4, I'll give it a go and if I get something that works post it back here in case it works.

I doubt many people are looking at this topic but you never know who might benefit later on....

Thanks,

Nick

---

<div class="post-metadata">

**Author:** ![mazeyrat](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mazeyrat/32/10955_2.png) [@mazeyrat](https://forum.inductiveautomation.com/u/mazeyrat)\
**Post date:** [December 13, 2022, 6:50pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/4 "2022-12-13T18:50:22Z")

</div>

I am interested in building a module to add a new kind of History. All post around this topic are very valuable for me !  
Thanks a lot.

---

<div class="post-metadata">

**Author:** ![nicholas.robinson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.robinson/32/22188_2.png) [@nicholas.robinson](https://forum.inductiveautomation.com/u/nicholas.robinson)\
**Post date:** [December 13, 2022, 10:16pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/5 "2022-12-13T22:16:48Z")

</div>

Here is how our Kafka module works (at least the tag history portion, there is also alarms and audit).

To test if the change I made today works or not, I just make a new tag and set the storage provider. Then any tag changes are sent to Kafka:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/a/da8946410cc4289105d76af0454e561c42c404c7.png)

Then if I go look in the Kafka broker I can see that the data is there. So that means with the new class filter applied, the records that _should_ be sent down the pipeline are actually able to make it. That is good.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/a/2a3ffe6b645b712e24213c5ad975f26ef8c18993.png)

Now comes the new part. I am expecting that when I delete that tag, a tag retirement message attempts to be sent but is prevented from being able. A message should show up in the logs.

Before that I just check that at this point the S&F quarantine for the tag history sink is empty, which it is:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/9/89342e8d18bf204db5bce528919768e05a8b7e88.png)

After deleting the tag, a message does show up in the logs. I supposed it could be useful to see more details so I may add additional log information, but this is sufficient to show that the _instanceof_ method added will prevent unwanted records from filling up the S&F quarantine:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/0/b0b764e8fc78824dbbc983b87f4a3a440aa779f4.png)

Here is the modified code:

```auto
@Override
public void storeData(HistoricalData data) throws IOException {
    int pathIndex = 0;

    for (HistoricalData row : BasicDataTransaction.class.cast(data).getData()) {
        // prevent TagRetirementList from going to store and forward quarantine
        if (row instanceof BasicScanclassHistorySet) {
            BasicScanclassHistorySet scanset = BasicScanclassHistorySet.class.cast(row);
            if (scanset.size() == 0) continue;

            String gatewayName = this.hostName;
            String provider = scanset.getProviderName();
            pathIndex = provider.length() + 2;

                for (HistoricalTagValue tagValue : scanset) {
                    try{
                        String json = new JSONObject()
                                .put("gatewayName", gatewayName)
                                .put("provider", provider)
                                .put("tagPath", tagValue.getSource().toString().replace("["+provider+"]", ""))
                                .put("type", tagValue.getTypeClass())
                                .put("quality", tagValue.getQuality())
                                .put("value", String.valueOf(tagValue.getValue()))
                                .put("epochms", tagValue.getTimestamp().getTime())
                                .toString();

                        SinkData value = new SinkData(topic, json, this.getPipelineName());
                        this.sendDataWithProducer(value);

                    } catch (JSONException e) {
                        logger.error("Error sending tag: " + e.toString());
                    }
                }
            } else {
                logger.info("Not storing data with the following class: " + row.getClass().toString());
            };
        }
    if (pathIndex > 0) {
        setLastMessageTime(this.name);
    }
}

```

@paul-griffith thanks for your help as always.

Rgds,

Nick

---

<div class="post-metadata">

**Author:** ![leonard.smit](https://avatars.discourse-cdn.com/v4/letter/l/cab0a1/32.png) [@leonard.smit](https://forum.inductiveautomation.com/u/leonard.smit)\
**Post date:** [December 16, 2025, 9:56pm UTC](https://forum.inductiveautomation.com/t/tagretirementlist-vs-basicscanclasshistoryset/67566/6 "2025-12-16T21:56:48Z")

</div>

Thanks for this post!

How would this be impacted by version 8.3? It seems that the History sink now expect

`AbstractSink<BasicTagGroupHistorySet>` - and not HistoricalData? I need to now how to handle `TagRetirementList` in custom sinks for verion 8.3

Thanks!
