Coding Challenge

The instruction could be found here.

The application is served by Heroku:

OpenAPI Documentation

Swashbuckle library provides the OpenAPI Specification previously known as the Swagger Specification to serve a contract of API.

Authorization

The service uses the custom implementation of the API-KEY authentication in conjunction with the Role Based authorization in order to limit the access to API. It allows to handle a different set of roles based on keys. You can use the following API key for testing purposes: 034ede0baffa42e4a1186f88ff2b825b

FullText Search

As the main full text search engine is used Lucene.NET. Also as an alternative it was implemented a custom engine based on Trigrams. The advantage of the Trigrams engine is simplicity when the disadvantage is twice the memory and CPU consumption compared to Lucene.NET engine.

Continuous Integration

Github Actions was chosen to build the code and to run all verifications.

Continuous Delivery

Docker packs the service a container.

docker build -t dominos-pizza-vouchers-api -f Dockerfile .
docker run -it -p 5000:80 dominos-pizza-vouchers-api

Heroku serves the docker container.

heroku container:login
heroku container:push web -a dominos-pizza-vouchers-api
heroku container:release web -a dominos-pizza-vouchers-api

Service: https://sk-dominos-pizza-vouchers-api.herokuapp.com/swagger/index.html

Testing

Performance

BenchmarkDotNet checks the performance of in-memory implementations: VoucherRepository, LuceneVouchersSearch and TrigramVoucherSearch. In order to get the local results you can run the following tests here.

Vouchers Repository

It is very fast and does not consume memory.

Method N Mean Error StdDev Ratio Gen 0 Allocated
GetVouchers 100 60.68 ns 1.274 ns 1.743 ns 1.00 0.0153 128 B
GetVoucherById 100 10.22 ns 0.053 ns 0.044 ns 1.00 - -
GetVouchersByName 100 112.12 ns 2.075 ns 1.941 ns 1.00 0.0191 160 B
GetCheapestVoucherByProductCode 100 16.17 ns 0.091 ns 0.085 ns 1.00 - -
GetVouchers 1000 195.88 ns 3.984 ns 6.202 ns 1.00 0.0420 352 B
GetVoucherById 1000 10.19 ns 0.265 ns 0.261 ns 1.00 - -
GetVouchersByName 1000 115.20 ns 2.344 ns 2.407 ns 1.00 0.0191 160 B
GetCheapestVoucherByProductCode 1000 16.59 ns 0.398 ns 0.517 ns 1.00 - -
GetVouchers 10000 185.55 ns 3.757 ns 4.885 ns 1.00 0.0420 352 B
GetVoucherById 10000 10.38 ns 0.108 ns 0.096 ns 1.00 - -
GetVouchersByName 10000 107.44 ns 1.465 ns 1.299 ns 1.00 0.0191 160 B
GetCheapestVoucherByProductCode 10000 16.97 ns 0.330 ns 0.258 ns 1.00 - -

Lucene Vouchers Search

It is very fast and consumes little memory.

Method N Mean Error StdDev Ratio Gen 0 Gen 1 Allocated
Search 1000 73.57 us 1.434 us 1.962 us 1.00 19.7754 4.8828 162 KB
Search 10000 69.75 us 0.876 us 0.820 us 1.00 19.8975 3.6621 163 KB
Search 100000 92.98 us 1.803 us 1.771 us 1.00 35.1563 7.3242 288 KB

Trigrams Vouchers Search

It is very fast and consumes less memory per operation comparing to Lucene, but twice the overall memory required.

Method N Mean Error StdDev Ratio Gen 0 Gen 1 Allocated
Search 1000 3.566 us 0.0253 us 0.0211 us 1.00 4.1809 0.4158 34 KB
Search 10000 3.549 us 0.0116 us 0.0102 us 1.00 4.1809 0.4158 34 KB
Search 100000 3.563 us 0.0200 us 0.0177 us 1.00 4.1809 0.4158 34 KB

High Load

The k6 is very powerfull to run the load tests. There are 2 tests to check throughput and latency:

To measure getting all vouchers.

Script:

k6 run -u 15 -d 15s ./k6/vouchers/search.js

Result:

http_req_waiting...............: avg=457.51µs min=0s med=506.7µs max=11.35ms p(90)=1ms p(95)=1ms
http_reqs......................: 355528  23697.385807/s

To measure searching vouchers.

Script:

k6 run -u 15 -d 15s ./k6/vouchers/search.js

Result using Lucene:

http_req_waiting...............: avg=9.42ms  min=513.29µs med=9.47ms max=98.39ms  p(90)=13.67ms p(95)=14.75ms
http_reqs......................: 23286   1551.494041/s

Result using Trigrams:

http_req_waiting...............: avg=13.37ms min=0s med=13.16ms max=107.11ms p(90)=20.5ms  p(95)=22.91ms
http_reqs......................: 16508   1099.754487/s

According to the results, Lucene 1.5x handles search queries better than Trigrams.