1

I am developing an application in react-native + Cloud Firestore, which aims to generate random numbers/tickets for each user who fill out a form. These numbers will allow the user to participate in an electronic lottery and compete for the prizes of a promotion.

These numbers should be separated by series, for example:

Series 1 - numbers can range from 0 to 99,999 thousand.
Series 2 - numbers can range from 0 to 99,999 thousand.
Series N - numbers can range from 0 to 99,999 thousand.

The system requirements are:

  • The series and numbers generated for each user must be maintained (numbers saved for the Client);
  • Each promotion must also contain the generated numbers and series (Numbers generated in the Promotion, Total);

In a first structure in Cloud Firestore, we created a structure as follows:

Promotion - Series - Number
(random doc) - 01 - 00131 ...
(random doc) - 01 - 97879 ...
(random doc) - 09 - 99999

Promotion is a collection, the Series is a promotion sub-collection, and Number is a Series sub-collection.

However, in this way many queries have been made in the firestore API, for example, to obtain the available numbers to be generated within a series, so that a new number can be generated.

Another approach we have found is that the subcollection of numbers could be an array within the Series collection. This would reduce queries.

Will the 100,000 number array exceed the maximum 1mb document size? It could be used a structure with map, for example, to write beyond the number other properties as follows:

Promotion - Series - Numbers: Map?
Dan McGrath
  • 41,220
  • 11
  • 99
  • 130

1 Answers1

0

"Will the 100,000 number array exceed the maximum 1mb document size?"

Each number in the array will consume 8 bytes, so just the array values will be ~800KB, then with some overhead for the document and field name you should still be under the limit.

You will, however, need to ensure you go into Single Field Indexes in the UI and create an exemption for the array field as each array value consumes 2 index entries (and the whole array consumes 2 itself), so you will definitely exceed the 20K index entries per document limit. Note: This means you won't be able to query the array.

"However, in this way many queries have been made in the firestore API, for example, to obtain the available numbers to be generated within a series, so that a new number can be generated."

I'm guessing these 2 unwritten assumptions:

  1. The same number cannot be given to more than a single client
  2. Most, if not all of the numbers in a series will be assigned

If 2 isn't true, you could just generate a number, see if it exists and if so regenerate and try again. This becomes more costly if most or all of the numbers will be assigned.

An alternative is to do a hybrid approach.

For 100,000 numbers, create 1000 documents that own 100 numbers each and store those 100 numbers in an array called ticket_number:

/promotions/<series number>/numbers/<nnnn>
--> ticket_number: [nnnn01, nnnn02, ..., (nnnn+1)00]

On each document have a boolean field called available that is set to true if numbers in that range are still available. If they are not, delete the field or set it to false

Use the method outlined here to select a random document from these documents where full == true. From this document select a random ticket number from the array, removing it and updating the document.

Eventually, all values will be selected this way, while only needing to read and write a single document for each ticket (as well as writing selected number/series into a separate document).

Write rate considerations

Note that it is recommended to have a maximum sustained rate of 1 write per second per document. In the single document approach, this would mean either a slow assignment of numbers or some upstream batching mechanism.

By using the hybrid approach you can shard across more documents to increase the write rate possible. In the example I give above, that's 1000 writes per second.

Community
  • 1
  • 1
Dan McGrath
  • 41,220
  • 11
  • 99
  • 130