Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating sequential numbers in multi-user saas application

How do people generate auto_incrementing integers for a particular user in a typical saas application?

For example, the invoice numbers for all the invoices for a particular user should be auto_incrementing and start from 1. The rails id field can't be used in this case, as it's shared amongst all the users.

Off the top of my head, I could count all the invoices a user has, and then add 1, but does anyone know of any better solution?

like image 911
Abhishiv Saxena Avatar asked Jul 09 '09 15:07

Abhishiv Saxena


4 Answers

Typical solution for any relation database could be a table like

user_invoice_numbers (user_id int primary key clustered, last_id int)

and a stored procedure or a SQL query like

update user_invoice_numbers set last_id = last_id + 1 where user_id = @user_id
select last_id from user_invoice_numbers where user_id = @user_id

It will work for users (if each user has a few simultaneously running transactions) but will not work for companies (for example when you need companies_invoice_numbers) because transactions from different users inside the same company may block each other and there will be a performance bottleneck in this table.

The most important functional requirement you should check is whether your system is allowed to have gaps in invoice numbering or not. When you use standard auto_increment, you allow gaps, because in most database I know, when you rollback transaction, the incremented number will not be rolled back. Having this in mind, you can improve performance using one of the following guidelines

1) Exclude the procedure that you use for getting new numbers from the long running transactions. Let's suppose that insert into invoice procedure is a long running transaction with complex server-side logic. In this case you first acquire a new id , and then, in separate transaction insert new invoice. If last transaction will be rolled back, auto-number will not decrease. But user_invoice_numbers will not be locked for long time, so a lot of simultaneous users could insert invoices at the same time

2) Do not use a traditional transactional database to store the data with last id for each user. When you need to maintain simple list of keys and values there are lot of small but fast database engines that can do that work for you. List of Key/Value databases. Probably memcached is the most popular. In the past, I saw the projects where simple key/value storages where implemented using Windows Registry or even a file system. There was a directory where each file name was the key and inside each file was the last id. And this rough solution was still better then using SQL table, because locks were issued and released very quickly and were not involved into transaction scope.

Well, if my proposal for the optimization seems to be overcomplicated for your project, forget about this now, until you will actually run into performance issues. In most projects simple method with an additional table will work pretty fast.

like image 152
Bogdan_Ch Avatar answered Nov 11 '22 11:11

Bogdan_Ch


You could introduce another table associated with your "users" table that tracks the most recent invoice number for a user. However, reading this value will result in a database query, so you might as well just get a count of the user's invoices and add one, as you suggested. Either way, it's a database hit.

like image 26
John Topley Avatar answered Nov 11 '22 13:11

John Topley


If the invoice numbers are independent for each user/customer then it seems like having "lastInvoice" field in some persistent store (eg. DB record) associated with the user is pretty unavoidable. However this could lead to some contention for the "latest" number.

Does it really matter if we send a user invoices 1, 2, 3 and 5, and never send them invoice 4? If you can relax the requirement a bit.

If the requirement is actually "every invoice number must be unique" then we can look at all the normal id generating tricks, and these can be quite efficient.

Ensuring that the numbers are sequenctial adds to the complexity, does it add to the business benefit?

like image 42
djna Avatar answered Nov 11 '22 13:11

djna


I've just uploaded a gem that should resolve your need (a few years late is better than never!) :)

https://github.com/alisyed/sequenceid/

like image 1
Syed Ali Avatar answered Nov 11 '22 12:11

Syed Ali