Skip to content

bernadev91/car-pooling-challenge

Repository files navigation

Documentation

Technologies used

PHP 8.1.2 and Laravel 10.10

Data persistence

To use the full capabilities of Laravel, it's necessary to use a Database. That's why a sqlite DB has been used. Other options like MongoDB or using data files could have been an option, but that's not the natural way to use Laravel and it would have made the code less representative of how a Laravel application in production usually looks like.

It makes sense to store the list of cars in a relational database of some sort, because in a real application, there would be additional data and relationships like who's the driver, path to the image file, registration plate, etc. But the list of journeys is just a FIFO Queue, and it could be stored in the database only after the passengers have been dropped off and the journey is complete, and it would not need to be done in real time necessarily. Therefore, something based on a regular Queue data structure using memcached might have served the purpose. But again, Laravel is not meant to be used for scenarios like that, so it made more sense to implement the Queue in a more naïve way: just using a database and making sure that the requests are stored and processed in the right order.

Implementation details

There's a lot of code generated by Laravel, and some of it is not used for this application.

The main files that implement the solution are located in:

  • app\Http\Controllers\PoolController.php: it handles the requests, makes sure they have the right format and returns the required data and HTTP codes.
  • app\Libraries\Carpooling.php: it handles the logic to assign cars to drivers.
  • app\Models\Car.php: Eloquent Model that is used to store the data and also manage the request that updates the list of cars.
  • app\Models\Journey.php: Eloquent Model that is used to store the journey data, and also manage the requests related to journeys like drop off or requesting a new journey.

Some other files were used to define the data model (in database/migrations), and to establish the routing (routes\api.php).

The challenge has been completed trying to follow a TDD approach.

  • First, the EndpointsTest test was created, making sure that each endpoint returns the right http codes and data, given a certain input
  • After all the endpoints worked separately, an integration test named WorkflowTest was created, with the intention of testing the whole process of creating cars, requesting journeys, and getting people assigned to the available cars. It checks that a waiting group gets assigned a car as soon as another group has been dropped off and is now available.
  • To test the scalability requirement ($10^4$ / $10^5$ cars / waiting groups.), and additional test was created, MassiveDataTest. It creates that number of cars and journeys to make sure that the system remains stable.

The Dockerfile has been updated to install the dependencies the Laravel application requires. It has been adapted from a previous project in production, with the only peculiarity of using a sqlite database instead of MySQL.

The docker folder contains the configuration files that will be copied to the container, along with some files that are useful for debugging.

Scalability and performance requirement

To test the performance of the application under heavy load, the MassiveDataTest test was created. The creation of 10^4 cars was never an issue because it handled the request in much less than a second. Creating the journeys was more time consuming because it required one HTTP request for each. The test showed that the development machine was capable of handling 1,000 sequential requests per second, with the system remaining stable during the whole process.

A cache has been used to store what's the vehicle with the highest number of seats. That will allow the system to validate incoming journey requests and return an error if, for example, there's a group of 8 people, but there's no car in the fleet with 8 seats. That avoids having to check the list of cars for each journey request.


Car Pooling Service Challenge Specification

Design/implement a system to manage car pooling.

At Cabify we provide the service of taking people from point A to point B. So far we have done it without sharing cars with multiple groups of people. This is an opportunity to optimize the use of resources by introducing car pooling.

You have been assigned to build the car availability service that will be used to track the available seats in cars.

Cars have a different amount of seats available, they can accommodate groups of up to 4, 5 or 6 people.

People requests cars in groups of 1 to 6. People in the same group want to ride on the same car. You can take any group at any car that has enough empty seats for them. If it's not possible to accommodate them, they're willing to wait until there's a car available for them. Once a car is available for a group that is waiting, they should ride.

Once they get a car assigned, they will journey until the drop off, you cannot ask them to take another car (i.e. you cannot swap them to another car to make space for another group).

In terms of fairness of trip order: groups should be served as fast as possible, but the arrival order should be kept when possible. If group B arrives later than group A, it can only be served before group A if no car can serve group A.

For example: a group of 6 is waiting for a car and there are 4 empty seats at a car for 6; if a group of 2 requests a car you may take them in the car. This may mean that the group of 6 waits a long time, possibly until they become frustrated and leave.

Evaluation rules

This challenge has a partially automated scoring system. This means that before it is seen by the evaluators, it needs to pass a series of automated checks and scoring.

Checks

All checks need to pass in order for the challenge to be reviewed.

  • The acceptance test step in the .gitlab-ci.yml must pass in master before you submit your solution. We will not accept any solutions that do not pass or omit this step. This is a public check that can be used to assert that other tests will run successfully on your solution. This step needs to run without modification
  • "further tests" will be used to prove that the solution works correctly. These are not visible to you as a candidate and will be run once you submit the solution

Scoring

There is a number of scoring systems being run on your solution after it is submitted. It is ok if these do not pass, but they add information for the reviewers.

API

To simplify the challenge and remove language restrictions, this service must provide a REST API which will be used to interact with it.

This API must comply with the following contract:

GET /status

Indicate the service has started up correctly and is ready to accept requests.

Responses:

  • 200 OK When the service is ready to receive requests.

PUT /cars

Load the list of available cars in the service and remove all previous data (reset the application state). This method may be called more than once during the life cycle of the service.

Body required The list of cars to load.

Content Type application/json

Sample:

[
  {
    "id": 1,
    "seats": 4
  },
  {
    "id": 2,
    "seats": 6
  }
]

Responses:

  • 200 OK When the list is registered correctly.
  • 400 Bad Request When there is a failure in the request format, expected headers, or the payload can't be unmarshalled.

POST /journey

A group of people requests to perform a journey.

Body required The group of people that wants to perform the journey

Content Type application/json

Sample:

{
  "id": 1,
  "people": 4
}

Responses:

  • 200 OK or 202 Accepted When the group is registered correctly
  • 400 Bad Request When there is a failure in the request format or the payload can't be unmarshalled.

POST /dropoff

A group of people requests to be dropped off. Whether they traveled or not.

Body required A form with the group ID, such that ID=X

Content Type application/x-www-form-urlencoded

Responses:

  • 200 OK or 204 No Content When the group is unregistered correctly.
  • 404 Not Found When the group is not to be found.
  • 400 Bad Request When there is a failure in the request format or the payload can't be unmarshalled.

POST /locate

Given a group ID such that ID=X, return the car the group is traveling with, or no car if they are still waiting to be served.

Body required A url encoded form with the group ID such that ID=X

Content Type application/x-www-form-urlencoded

Accept application/json

Responses:

  • 200 OK With the car as the payload when the group is assigned to a car. See below for the expected car representation
  {
    "id": 1,
    "seats": 4
  }
  • 204 No Content When the group is waiting to be assigned to a car.
  • 404 Not Found When the group is not to be found.
  • 400 Bad Request When there is a failure in the request format or the payload can't be unmarshalled.

Tooling

At Cabify, we use Gitlab and Gitlab CI for our backend development work. In this repo you may find a .gitlab-ci.yml file which contains some tooling that would simplify the setup and testing of the deliverable. This testing can be enabled by simply uncommenting the final acceptance stage. Note that the image build should be reproducible within the CI environment.

Additionally, you will find a basic Dockerfile which you could use a baseline, be sure to modify it as much as needed, but keep the exposed port as is to simplify the testing.

⚠️ Avoid dependencies and tools that would require changes to the acceptance step of .gitlab-ci.yml, such as docker-compose

⚠️ The challenge needs to be self-contained so we can evaluate it. If the language you are using has limitations that block you from solving this challenge without using a database, please document your reasoning in the readme and use an embedded one such as sqlite.

You are free to use whatever programming language you deem is best to solve the problem but please bear in mind we want to see your best!

You can ignore the Gitlab warning "Cabify Challenge has exceeded its pipeline minutes quota," it will not affect your test or the ability to run pipelines on Gitlab.

Requirements

  • The service should be as efficient as possible. It should be able to work reasonably well with at least $10^4$ / $10^5$ cars / waiting groups. Explain how you did achieve this requirement.
  • You are free to modify the repository as much as necessary to include or remove dependencies, subject to tooling limitations above.
  • Document your decisions using MRs or in this very README adding sections to it, the same way you would be generating documentation for any other deliverable. We want to see how you operate in a quasi real work environment.

Feedback

In Cabify, we really appreciate your interest and your time. We are highly interested on improving our Challenge and the way we evaluate our candidates. Hence, we would like to beg five more minutes of your time to fill the following survey:

Your participation is really important. Thanks for your contribution!

About

Cabify technical challenge using Laravel

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published