3
\$\begingroup\$

One of the companies I was recently interviewing with gave me a following task to implement in a day or two using .Net, just to see where my coding abilities are at:

Implement a Web API that gets webhook on POST and enters it to DB

  1. Enter the data first to collection, then read from this collection (other thread), and enter it to DB
  2. We want to keep the order of the webhooks
  3. In order to prevent loss of data, think of a mechanism that backups to file on restart

Sample WebHook payload:

{
    "date": "2022-01-01T02:01:30",
    "json": "{ \"a\":\"b\", \"c\":\"d\" }"
}

My solution:

The idea of the implementation was to create a singleton object that will hold a concurrent queue that fills up as post requests are received.

public sealed class ConcurrentQueueSingleton
    {
        public ConcurrentQueue<WebHookInfoDto> WebHookInfoDtos { get; private set; }
        // private ctor so no-one can create an instance
        private ConcurrentQueueSingleton() {

            this.WebHookInfoDtos = new ConcurrentQueue<WebHookInfoDto>();
        }

        // static field to hold the ConcurrentQueueSingleton
        // initialization is thread-safe because it is handled by the static ctor
        private readonly static ConcurrentQueueSingleton instance = new ConcurrentQueueSingleton();

        // public static ConcurrentQueueSingleton instance
        public static ConcurrentQueueSingleton Instance
        {
            get { return instance; }
        }
    }

Then, there is a background task in which hosted scoped service every so often will consume the queue in a singleton one item at a time and save the data in the database.

Scoped processing service

 internal class ScopedProcessingService : IScopedProcessingService
    {
        private const int MillisecondsDelay = 30000;
        private int executionCount = 0;
        private readonly WebHookInfoRepository _webHookInfoRepository;

        public ScopedProcessingService( WebHookInfoRepository webHookInfoRepository)
        {
            _webHookInfoRepository = webHookInfoRepository;
        }

        public async Task DoWork(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                executionCount++;

                await Task.Delay(MillisecondsDelay);

                Debug.WriteLine(
                    $"Scoped Processing Service is working. Count: {executionCount}");
                await _webHookInfoRepository.StoreQueueToDb();
            }
        }
    }

Saving the queue to the db one item at a time:

public async Task StoreQueueToDb()
        {
            var queue = ConcurrentQueueSingleton.Instance.WebHookInfoDtos;
               
            while (!queue.IsEmpty)
            {               
                var canConnect = await context.Database.CanConnectAsync();
                if (canConnect)
                {
                    var dequeued = queue.TryDequeue(out var webHookInfoDto);
                    if (dequeued && webHookInfoDto != null)
                    {
                        WebHookInfoModel model = new WebHookInfoModel(webHookInfoDto.Date, webHookInfoDto.Json);
                        await context.AddAsync(model);
                        await context.SaveChangesAsync();
                    }

                }               

            }
        }

When application shutdowns, the contents of the queue are serialized to json and saved to a file, when it starts, it's contents are loaded in from the file.

I used code example provided by Microsoft official docs found here for the implementation of the background service, and John Skeet's example for the implementation of the thread safe singleton.

Complete code can be found here: https://github.com/mi105/WebHookQueue

The feedback I got is that solution is neither good enough neither complete. And that was about it.

Unfortunately, I could only spare a couple of hours on the solution, so I'm aware that stuff like exception handling, detailed logging and unit tests coverage are missing. I'm still wondering if there are some blunt oversights from my end.

Any constructive feedback from you guys on the solution I came up with and the code I wrote is welcome .

\$\endgroup\$
6
  • \$\begingroup\$ then read from this collection (other thread) do they mean to using the current thread for writing, and a second thread for reading ? or did they mean to use asynchronous tasks? \$\endgroup\$
    – iSR5
    Commented Jun 2, 2022 at 20:10
  • \$\begingroup\$ I actually asked the same question @iSR5. The response was reading from collection and writing to the db should be done on a separate thread from main api/writing to the collection thread. I interpreted it as it can be a background worker thread or a background service, but it has to be kept separate from the main api / write thread, and the invariant of keeping the order of the api requests when inserting them to the DB must be maintained. \$\endgroup\$
    – mi105
    Commented Jun 2, 2022 at 21:08
  • 1
    \$\begingroup\$ I have never encountered any such requirements. I'm wondering which case scenario lead them to consider that logic ?. curiosity is eating me :$. \$\endgroup\$
    – iSR5
    Commented Jun 2, 2022 at 23:05
  • \$\begingroup\$ Can't you ask for more detailed feedback from the hiring company? \$\endgroup\$ Commented Jun 3, 2022 at 7:29
  • \$\begingroup\$ @Peter Csala Did ask, but did not get it though. \$\endgroup\$
    – mi105
    Commented Jun 3, 2022 at 9:19

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.