0

I am integrating a third party web service which requires a unique integer for each call and once that integer is consumed, it does not accept that integer ever again. It will be a real-time integration, i-e there can be multiple calls to that web services from multiple users on the website this means that there can also be simultaneous calls if more than one user calls that web-service.

For now I tried PHP's uniqid() method to generate a unique id for each transaction but as it does not guarantee unique integers always.

Also I tried UNIX's Epoch seconds as required integer which works perfectly but I know it will not work with simultaneous calls.

I don't require random numbers, it can be anything which will never repeat. Please suggest possible solutions.

Zohaib Aslam
  • 336
  • 3
  • 13
  • How big is the space you can use (integer width)? Does it *have* to be an integer? – cv-pls May 06 '16 at 16:20
  • 1
    Unix epoch *plus* a large random number from `mt_rand` should make a duplicate integer extraordinarily unlikely. Something like `$id = time() . mt_rand(0, PHP_INT_MAX);` – ceejayoz May 06 '16 at 16:20
  • 32-bit integer is sufficient. and Yes it has to be an integer as per web-service requirement. – Zohaib Aslam May 06 '16 at 16:26

4 Answers4

3

Tons of solutions:

  • keep track of your integers (flat file, database with integer key, autoincrement)
  • prevent calls from being simultaneous (use locks, semaphores etc)
  • use queue for your calls
Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • I had these solutions in mind before posting this question but I hope that there is a more efficient way to do it without considering track of integers or using semaphores - which may lead to starvation when thousands of users are online at same time. – Zohaib Aslam May 06 '16 at 16:19
  • 1
    I dare to say that if you experience thousands of simultaneous users, then you will have more serious issues anyway :) – Marcin Orlowski May 06 '16 at 16:26
  • For now this is one serious issue. The call to Web-service deals with sending thousands of dollars. If a call will ever fail, that will be a very serious issue for the company for whom I am developing this website, and eventually, a serious issue for me :p – Zohaib Aslam May 06 '16 at 16:29
  • 1
    In that case you must use a queue so you can re-try failed requests. Design for failure. It will happen. – Asaph May 06 '16 at 16:30
  • If a call fails, you should handle that appropriately. You should never assume that all calls will succeed even with a unique id. – Devon Bessemer May 06 '16 at 16:31
  • Echoing @MarcinOrlowski 's remark on `more serious problems`. Do the math, with 32 bits your are collision-bound in no time flat (ie. while you are still at that job) :) – YvesLeBorg May 06 '16 at 16:31
  • Web-service accepts only 32-bit integer so that is not my problem how they will handle, maybe there is a threshold after which it allows repetition of integers I'll definitely confirm it. – Zohaib Aslam May 06 '16 at 16:43
  • Thank you @Asaph I'll consider your suggestion too! – Zohaib Aslam May 06 '16 at 16:44
  • @ZohaibAslam if you are not doing realtime call then you should add the queue otherwise connectivity problems can wreck havoc on your system sooner than you expect. – Marcin Orlowski May 06 '16 at 16:51
  • In another forum someone suggests that I should convert current time to integer narrowed down to micro-seconds. How is this approach? – Zohaib Aslam May 06 '16 at 17:06
  • @ZohaibAslam Bad as well. Chances two hits will be exactly at the same moment are quite unlikely on single machine, but add another box (you remember, thousands of users :) and you may end dead. Simply assume that **if** anything can happen it **will** happen. And be ready in advance. – Marcin Orlowski May 06 '16 at 17:14
  • So the only option that is left here is to **keep track of the integers**. I should be using this approach thank you for the help. – Zohaib Aslam May 06 '16 at 17:23
2

Create a database table with an auto-incrementing primary key and use that number. Insert a row for each call to the 3rd party service.

Update:

Based on your comment in another answer I recommend using a queue so that failures may be re-tried:

The call to Web-service deals with sending thousands of dollars. If a call will ever fail, that will be a very serious issue...

Your queue can either be implemented in a simple database table and use the primary key as mentioned above, or you can use a full-blown queue service.

Asaph
  • 159,146
  • 25
  • 197
  • 199
  • I hope that there will be more efficient solution. – Zohaib Aslam May 06 '16 at 16:16
  • 3
    How many inserts/second do you reasonably need for your use case? – Asaph May 06 '16 at 16:19
  • Who knows! there can be thousands of users online at same time using the web-service, more users, slower response. – Zohaib Aslam May 06 '16 at 16:23
  • You can pre-allocate batches of unique ids and store them locally on each server for efficient handling of bursts. – Asaph May 06 '16 at 16:25
  • I'll definitely consider this suggestions if I won't find anything more efficient. Thanks a lot. – Zohaib Aslam May 06 '16 at 16:30
  • In another forum someone suggests that I should convert current time to integer narrowed down to micro-seconds. How is this approach? – Zohaib Aslam May 06 '16 at 17:06
  • 1
    @ZohaibAslam I suspect using only the time, no matter what the granularity, poses a risk of collision. – Asaph May 06 '16 at 17:08
  • Yes, I understand, it just reduces the probability of collision but it can happen whatsoever. – Zohaib Aslam May 06 '16 at 17:15
  • It doesn't reduce the probability enough. It's a bad solution. Don't do it. – Asaph May 06 '16 at 17:16
  • Another flaw with using only the time is that it could pose a security threat. An attacker can easily guess the id of any transaction simply based on the time. – Asaph May 06 '16 at 17:17
  • So the only option that is left here is to **keep track of the integers**. I should be using this approach thank you for the help. Also I am planning to write another script to be run as a crone job which will keep deleting the junk rows. – Zohaib Aslam May 06 '16 at 17:26
2

As you mentioned that you have tried uniqid(), which does not produce an integer directly, I assume that what you need can be a string, and you would simply like it to be unique.

This is what UUID is for, and you can generate them in PHP using a library like this or a simpler approach like this.

Ah UUID is effectively a 128-bit integer, and you could convert it to decimal with a little effort if needed.

Community
  • 1
  • 1
cv-pls
  • 421
  • 2
  • 5
0

Use a counter with time and uniqid.

 $_SESSION['counter'] += 1;    
 $unique = time() . $_SESSION['counter'] . str_shuffle(rand(100,10000));

If 32 bits, then

$uniqMax = pow(2, 32);

Save in a db and check if it exists before unique code approval

Option 2: Use a seperate db that creates an id and supplies it upon request instead of inserting a row with a uniqid

Nitin
  • 898
  • 1
  • 9
  • 25