page_type | languages | products | name | urlFragment | description | ||
---|---|---|---|---|---|---|---|
sample |
|
|
Azure Cosmos DB design pattern: Preallocation Patern
|
preallocation |
This is an example that shows how preallocation is used to optimize the performance and efficiency of database operations by allocating resources or space in advance, rather than dynamically as needed. |
The pre-allocation pattern involves creating an initial empty structure that will be populated later. This approach simplifies the design of queries and logic compared to alternative methods. It should be noted that pre-allocating data can result in larger storage and RU usage but queries execute faster and the overall operations involved also tend to be faster as the query itself returns the necessary data versus having to manually merge data from multiple queries.
This sample demonstrates:
- ✅ A hotel reservation system using the preallocation pattern.
This pattern tends to go well with collections of dates or representations of values in an array that should be pre-created with a related item.
A couple examples include:
- Hotel room reservations for a room instance
- Available seats in a theatre for a movie instance.
NOTE Azure Cosmos DB has an item size limit of 2MB for an item. Be sure any document that you model with the pre-allocation pattern stays under this limit, if it goes over this limit, you will need to consider breaking the data into separate items.
The main components of the models used in the sample include:
- Hotels that contains Rooms
- Reservations that include a RoomId and HotelId
- Rooms can contain ReservationDates
In the non-preallocation pattern, you will see that a hotel is created with 10 rooms. These rooms have no reservations and the process of checking for reservations would be to query for all the rooms in the hotel, then querying for any existing reservations, then merging both datasets and subtracting out any rooms that have reservations for the dates being searched for.
- An example hotel:
{
"id": "hotel_1",
"EntityType": "hotel",
"hotelId": "hotel_1",
"Name": "Microsoft Hotels Inc",
"City": "Redmond",
"Address": "1 Microsoft Way",
"Rating": 0,
"AvailableRooms": 0,
"Rooms": [],
"Reservations": []
}
- An example room that does not utilize pre-allocation of available dates:
{
"id": "room_0",
"EntityType": "room",
"hotelId": "hotel_1",
"Name": null,
"Type": null,
"Status": null,
"NoBeds": 0,
"SizeInSqFt": 0,
"Price": 0,
"Available": false,
"Description": null,
"MaximumGuests": 0,
"Features": [],
"RoomImages": [],
"Reviews": []
}
- An example reservation item, where the room is a part of the reservation item:
{
"id": "reservation_room_0_20230213",
"EntityType": "reservation",
"LeaseId": null,
"LeasedUntil": null,
"IsPaid": false,
"Status": null,
"StartDate": "2023-02-13T00:00:00",
"EndDate": "2023-02-14T00:00:00",
"CheckIn": "0001-01-01T00:00:00",
"CheckOut": "0001-01-01T00:00:00",
"Customer": null,
"hotelId": "hotel_1",
"RoomId": "room_0",
"Room": {
"id": "room_0",
"EntityType": "room",
"LeaseId": null,
"LeasedUntil": null,
"hotelId": "hotel_1",
"Name": null,
"Type": null,
"Status": null,
"NoBeds": 0,
"SizeInSqFt": 0,
"Price": 0,
"Available": false,
"Description": null,
"MaximumGuests": 0,
"ReservationDates": [],
"Features": [],
"RoomImages": [],
"Reviews": []
}
}
In the following example you will see the reservation dates for a room being pre-allocated in a collection with a simple IsReserved
property for each date. This will then make the process of finding available dates easier and faster.
DateTime start = DateTime.Parse(DateTime.Now.ToString("01/01/yyyy"));
DateTime end = DateTime.Parse(DateTime.Now.ToString("12/31/yyyy"));
//add all the days for the year which can be queried later.
foreach (Room r in h.Rooms)
{
int count = 0;
while (start.AddDays(count) < end)
{
r.ReservationDates.Add(new ReservationDate { Date = start.AddDays(count), IsReserved = true });
count++;
}
}
- A pre-allocation room will now look similar to the following:
{
"id": "room_0",
"_etag": "\"de016f0a-0000-0700-0000-64f19c080000\"",
"EntityType": "room",
"LeaseId": null,
"LeasedUntil": null,
"hotelId": "hotel_1",
"Name": null,
"Type": null,
"Status": null,
"NoBeds": 0,
"SizeInSqFt": 0,
"Price": 0,
"Available": false,
"Description": null,
"MaximumGuests": 0,
"ReservationDates": [
{
"Date": "2023-09-01T00:00:00Z",
"IsReserved": true
},
{
"Date": "2023-09-02T00:00:00Z",
"IsReserved": false
},
{
"Date": "2023-09-03T00:00:00Z",
"IsReserved": false
},...
]
}
By pre-allocating the room's reservation days, you can easily run the following query to find available dates for a particular room or set of rooms:
SELECT
a.Date,
a.IsReserved,
r.hotelId
FROM room r
JOIN a IN r.ReservationDates
WHERE
a.Date >= '2023-09-01T00:00:00Z' AND
a.Date < '2023-09-02T00:00:00Z' AND
a.IsReserved = false AND
r.hotelId = 'hotel_1'
By not choosing the pre-allocation pattern, the alternative way to find available rooms for a set of dates will be more complex. For example, without pre-allocation, you would need to query all reservations for a room then build a collection of available dates by subtracting the reservation dates. You can see a subset of this logic available in the FindAvailableRooms
method of the Hotel
class.
To run this demo, you will need to have:
Confirm you have the required versions of the tools installed for this demo.
First, check the .NET runtime with this command:
dotnet --list-runtimes
As you may have multiple versions of the runtime installed, make sure that .NET components with versions that start with 8.0 appear as part of the output.
Directions installing pre-requisites to run locally and for cloning this repository using Terminal or VS Code
Open the application code in GitHub Codespaces:
You need to configure the application configuration file to run these demos.
-
Go to resource group.
-
Select the Serverless Azure Cosmos DB for NoSQL account that you created for this repository.
-
From the navigation, under Settings, select Keys. The values you need for the application settings for the demo are here.
While on the Keys blade, make note of the URI
and PRIMARy KEY
. You will need these for the sections below.
- Open the project and add a new appsettings.development.json file with the following contents:
{
"CosmosUri": "",
"CosmosKey": "",
"DatabaseName": "PreallocationDB",
"WithPreallocation": "WithPreallocation",
"WithoutPreallocation": "WithoutPreallocation"
}
-
Replace the
CosmosURI
andCosmosKey
with the values from the Keys blade in the Azure Portal. -
Save the file.
-
Open the application code.
-
From Codespaces or locally with Visual Studio Code, start the app by running the following:
dotnet build
dotnet run
or from Visual Studio, press F5 to start the application.
-
The application will automatically create a database called
PreallocationDB
with two containers,WithPreallocation
andWithoutPreallocation
. -
Select option
1
in the console application to load the hotel and room data. -
In Azure Portal, browse to the Azure Cosmos DB account for this respository.
-
Select Data Explorer in the left menu.
-
Locate and open the
PreallocationDB
-
Review the data in both containers. Notice different structure for both containers. The 'Reservation' entity type documents are not used in the
WithPreallocation
container. Instead the reservation dates for a room are pre-allocated in an array in each room document. -
Select option
2
to run the query with out Preallocation. Provide a date in DD-MM-YYYY format. Note the RU Charge and elapsed time. -
Select option
3
to run the same query using Preallocation. Provide a date in DD-MM-YYYY format. Note the RU Charge and elapsed time. -
Compare the code for both options. Notice that Preallocation allows for faster response times. However it often comes at a cost of higher RU charge due to the larger document sizes.
-
In the
Hotel.cs
view the queries for each method. Note the simpler design for queries and application logic using Preallocation versus when not using it.
Pre-allocation allows for a much simpler design for queries and logic versus other approaches. It can often yield faster reponse times as well. However it can come at the cost of a larger document in storage and RU charge given the pre-allocation of the data.