Uncategorized

Making periodic aggregated server calls on Android

If you’ve read the Jana technology blog before, you might have noticed that we are quite conscious about measuring things and making data-backed decisions. Whether A/B tests or user interactions, we collect every possible data point so that we can make informed decisions. Tracking every event can help you improve your app, but it can come with a big cost if not properly implemented.

Consider this scenario: a user is scrolling through a news feed with 100+ items. If you want to track what news items were actually shown to users, a naive implementation would be to make 100+ server calls, all recording a view event for each news item ID. In mobile applications, network calls are not free — there is a lot of latency and overhead with each call — so making 100+ is not a great idea. Here is a good article on how expensive those requests can get on a cellular network.

A better approach, in this scenario, is a system that aggregates events and makes periodic server calls with batches of events. The first component of this system is a thread-safe queue. All the events that need to be tracked are sent to a function that stores them in the queue. Since the queue is thread-safe it can be called simultaneously in multiple threads.

 

public class Client {
    private ConcurrentLinkedQueue<EventTrackRequest> aggregatedRequests = new ConcurrentLinkedQueue<EventTrackRequest>();
    
    ...

    public void trackEvent(EventTrackRequest req) {
        mBatchedRequests.add(req);
    }
}

public class SomeViewAdapter {
    @Override
    public void onBindViewHolder(feedViewHolder holder, int position){
        FeedItem feedItem = feedItems.get(position);
        client.trackEvent(new EventTrackRequest(feedItem.id, ….);
        showFeedItem(feedItem);
    }
}

 

Another component of this system is ScheduledThreadPoolExecutor object that runs periodically and makes server calls reporting the batch of events since the last run.

 

public class Client {
    private ScheduledThreadPoolExecutor mScheduler (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);

    ...
    private void startScheduler () {
        mScheduler.scheduleWithFixedDelay(
            new Runnable() {
                @Override
                public void run() {
                    reportAggregatedEvents();
                }
            },
            0,
            BATCH_DELAY_SECS,
            TimeUnit.SECONDS);
    }

    private void reportAggregatedEvents() {
        if (this.mBatchedRequests.isEmpty()) {
            return;
        }
        List<EventTrackRequest> aggregatedEvents = new ArrayList();   
        while (null != (event = this.aggregatedRequests.poll())) {
            aggregatedEvents.add(event);
        }
        sendToServer(aggregatedEvents);
    }
}

 

As you can see, this simple system design can provide a scalable solution for the event tracking problem. Hundreds of network requests can be consolidated into just one request, and you can avoid a lot of the overhead of making individual calls.

Discussion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s