.NET Tracker with multiple collector endpoints

We’ve implemented the .NET tracker and are sending events using the global singleton tracker instance. There are now certain situations where we’d like to send events to a Snowplow Micro instance for QA. This isn’t a separate environment, but a particular situation where we want to intercept the event and not send it to production.

In order to switch from our production tracker to Snowplow Micro, I look for the particular scenario where it should go to Micro and stop the tracker and restart with new collector URI. The reverse occurs if an event which should go to production then comes in. This works fine in the situation an environment is not receiving any production traffic and there is then a switchover after passing automated testing. However, I can see an edge case where a mixture of events occurs where some go to production and some to Micro. This would result in the tracker being repeatedly stopped and started with different endpoints which feels inefficient and likely to cause issues.

Is it possible to run two instances of the tracker and programmatically switch between the two? It looks like the use of a global singleton instance which is instantiated with the collector URI is intended to not allow this?

Hi @Michael_Bailey
That is indeed the case. We could remove the Singleton restriction, this is a pattern that varies across our trackers depending on common practices within different languages and frameworks. However, we generally see the Singleton pattern works well for multithreaded server applications as it reduces the risk of multiple threads spawning lots of Trackers and the internal buffers rather than a central one which can then batch events to reduce the number of POSTs.

Usually we don’t see a backend service that accepts both non-production and production events in the same instance of that service. Most would run in a separate environment and then the collector url would be controlled via some sort of environment variable. Is that not the case here?

The only way to programmatically stop and switch is to call Tracker.stop() to ensure the queue is flushed to the correct collector and then call Tracker.start() with a new IEmitter. This is the case as the events aren’t tied to a specific Emitter, they are added to a buffer and then asynchronously sent to the collector.

Hi Paul,

Thanks for the quick reply. The particular scenario here is that we update a production system which is not receiving any user traffic. We want to automate verifying the Snowplow configuration is as expected before live traffic begins hitting the servers again. This is not a staging site, but a live production system (although not currently serving real users). Assuming all tests pass, we stop sending requests to Micro when the system returns to serving production traffic. In this scenario, starting and stopping the tracker seems like an acceptable solution.

My concern is if we were to run tests against an environment which was still receiving production traffic we’d have the potential for a whole series of stop / start tracker commands in quick succession as a mixture of live and test traffic hit the system. We can prevent this in other ways, but it would be good to know if there is a better way to implement the ability for an environment to support two collectors concurrently.


Currently, no there isn’t a technique that will allow for this in the .Net tracker due to the Tracker singleton. You’ll need to flush out the tracker using stop() and kick off again with start() when new events arrive. I hope this is ok for your testing use case as hopefully you won’t see a mix of test and production events at the same time.

This point does raise the question of if singletons are the right design choice here or if we should defer that decision to the users of the library. I think that’s a valid point and solid feedback for how we should iterate the .Net tracker going forward. The server side trackers are on our roadmap later this year to get some updates, and I think the .Net tracker is an important one on that list.