Implement an analytics SDK that exposes log events, it takes in events and queues them, and then starts sending the events. This is a Flipkart frontend interview question.
- Send each event after a delay of 1 second and this logging fails every n % 5 times.
- Send the next event only after the previous one resolves.
- When the failure occurs attempt a retry.
Example
Input: const sdk = new SDK(); sdk.logEvent("event 1"); sdk.logEvent("event 2"); sdk.logEvent("event 3"); sdk.logEvent("event 4"); sdk.logEvent("event 5"); sdk.logEvent("event 6"); sdk.logEvent("event 7"); sdk.logEvent("event 8"); sdk.logEvent("event 9"); sdk.logEvent("event 10"); sdk.send(); Output: "Analytics sent event 1" "Analytics sent event 2" "Analytics sent event 3" "Analytics sent event 4" "-----------------------" "Failed to send event 5" "Retrying sending event 5" "-----------------------" "Analytics sent event 5" "Analytics sent event 6" "Analytics sent event 7" "Analytics sent event 8" "-----------------------" "Failed to send event 9" "Retrying sending event 9" "-----------------------" "Analytics sent event 9" "Analytics sent event 10"
Breaking the problem statement into subproblems we can create this SDK in three steps.
Delay function
The most important part of this function is that the events will be sent after a delay of 1 second and fails n%5
times.
We can do the same by extending the sleep function.
Create a new Promise and inside that run a setTimeout that will resolve the promise.
To the same, we will add one extra condition that will check if the current execution is n%5
then reject, else resolve.
// function to delay the exection wait = () => new Promise((resolve, reject) => { setTimeout(() => { // reject every n % 5 time if(this.count % 5 === 0){ reject(); } else { resolve(); } }, 1000); });
Queue the events
We have to store the events so that they can be sent one by one also we need a tracker so that we can reject for each n%5
.
We can create a class and initialize these in the constructor.
class SDK { constructor(){ // hold the events this.queue = []; // track the count this.count = 1; } // push event in the queue logEvent(ev) { this.queue.push(ev); } }
Sending the events
The final part is sending the events, for this, we can create a helper function, that will recursively call itself and keep on sending one-one events every time.
This will be an async function and in each call, get the first element from the queue and try the wait()
, if it resolves then print the log or perform any other operation, else if it fails, push the event back in the queue for retry. Finally, recursively call the same function for the next operation.
Add a base case to stop the execution if there are no more events in the queue. Also, track the count in each call.
// to send analytics // recursively send the events sendAnalytics = async function (){ // if there are no events in the queue // stop execution if(this.queue.length === 0){ return; } // get the first element from the queue const current = this.queue.shift(); try { // delay await this.wait(); // print the event // can perform any other operations as well like making api call console.log("Analytics sent " + current); // increase the count this.count++; } catch(e){ // if execution fails console.log("-----------------------"); console.log("Failed to send " + current); console.log("Retrying sending " + current); console.log("-----------------------"); // reset the count this.count = 1; // push the event back into the queue this.queue.unshift(current); }finally{ // recursively call the same function // to send the remaining this.sendAnalytics(); } } // start the execution send = async function(){ this.sendAnalytics(); }
Putting everything together
class SDK { constructor(){ // hold the events this.queue = []; // track the count this.count = 1; } // push event in the queue logEvent(ev) { this.queue.push(ev); } // function to delay the exection wait = () => new Promise((resolve, reject) => { setTimeout(() => { // reject every n % 5 time if(this.count % 5 === 0){ reject(); } else { resolve(); } }, 1000); }); // to send analytics // recursively send the events sendAnalytics = async function (){ // if there are no events in the queue // stop execution if(this.queue.length === 0){ return; } // get the first element from the queue const current = this.queue.shift(); try { // delay await this.wait(); // print the event // can perform any other operations as well like making api call console.log("Analytics sent " + current); // increase the count this.count++; } catch(e){ // if exection fails console.log("-----------------------"); console.log("Failed to send " + current); console.log("Retrying sending " + current); console.log("-----------------------"); // reset the count this.count = 1; // push the event back into the queue this.queue.unshift(current); }finally{ // recursively call the same function // to send the remaining this.sendAnalytics(); } } // start the execution send = async function(){ this.sendAnalytics(); } }
Input: const sdk = new SDK(); sdk.logEvent("event 1"); sdk.logEvent("event 2"); sdk.logEvent("event 3"); sdk.logEvent("event 4"); sdk.logEvent("event 5"); sdk.logEvent("event 6"); sdk.logEvent("event 7"); sdk.logEvent("event 8"); sdk.logEvent("event 9"); sdk.logEvent("event 10"); sdk.logEvent("event 11"); sdk.logEvent("event 12"); sdk.logEvent("event 13"); sdk.logEvent("event 14"); sdk.logEvent("event 15"); sdk.logEvent("event 16"); sdk.logEvent("event 17"); sdk.logEvent("event 18"); sdk.logEvent("event 19"); sdk.logEvent("event 20"); sdk.send(); Output: "Analytics sent event 1" "Analytics sent event 2" "Analytics sent event 3" "Analytics sent event 4" "-----------------------" "Failed to send event 5" "Retrying sending event 5" "-----------------------" "Analytics sent event 5" "Analytics sent event 6" "Analytics sent event 7" "Analytics sent event 8" "-----------------------" "Failed to send event 9" "Retrying sending event 9" "-----------------------" "Analytics sent event 9" "Analytics sent event 10" "Analytics sent event 11" "Analytics sent event 12" "-----------------------" "Failed to send event 13" "Retrying sending event 13" "-----------------------" "Analytics sent event 13" "Analytics sent event 14" "Analytics sent event 15" "Analytics sent event 16" "-----------------------" "Failed to send event 17" "Retrying sending event 17" "-----------------------" "Analytics sent event 17" "Analytics sent event 18" "Analytics sent event 19" "Analytics sent event 20"