-
Notifications
You must be signed in to change notification settings - Fork 25
Description
After upgrading to 3.1.0, I am getting some unexpected output from the following test code:
$ uv pip list | grep ulid
python-ulid 3.1.0
$ python3
Python 3.13.5 (main, Jun 11 2025, 15:36:57) [Clang 19.1.7 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from ulid import ULID
>>> miss = 0
>>> prev = ""
>>> miss=0
... for i in range(5000):
... n = str(ULID())
... if n < prev:
... miss+=1
... print(f"next {n} < prev {prev} !! {miss}")
... prev=n
...
next 01K37AJA5QDYD4BP0K7WP36YBR < prev 01K37AJA5QJR2P2YEZV244F2WQ !! 1
next 01K37AJA5XHK1QAT1VKWG2AMX6 < prev 01K37AJA5XXC3CV5Z9M7T9WHR1 !! 2
>>>This shows that the IDs are not following monotonic sort order.
Given the fact that for the first miss case, the IDs both start with 01K37AJA5Q (i.e. the timestamp), I suspect that the issue is somewhere in the new feature as mentioned in the 3.1.0 changelog:
When generating ULIDs within the same millisecond, the library will ensure monotonic sort order by incrementing the randomness component by 1 bit.
Looking as some context around the miss
When printing out the generated IDs, there is a clear jump in the output:
(the number at the start is the miss counter)
0 01K37BHXZ8REXFPRZW7KW6NDGC
0 01K37BHXZ8REXFPRZW7KW6NDGD
0 01K37BHXZ8REXFPRZW7KW6NDGE
1 01K37BHXZ8CQKKDCNFZ8EH5SXV
1 01K37BHXZ9CQKKDCNFZ8EH5SXW
1 01K37BHXZ9CQKKDCNFZ8EH5SXX
These all carry the same timestamp. Before and after the jump, there is incrementing going on on the random part, so that looks like the new feature in action.
In the same run, I also got:
1 01K37BHXZ9CQKKDCNFZ8EH5SYT
1 01K37BHXZ9CQKKDCNFZ8EH5SYV
1 01K37BHXZ9CQKKDCNFZ8EH5SYW
2 01K37BHXZ98VYMQHC24B0TNB9V
2 01K37BHXZA8VYMQHC24B0TNB9W
2 01K37BHXZA8VYMQHC24B0TNB9X
--------8<--------
2 01K37BHY08YGRGZNCG5B264WRG
2 01K37BHY08YGRGZNCG5B264WRH
2 01K37BHY08YGRGZNCG5B264WRJ
3 01K37BHY08HV9Y6H8VHCFNTQRM
3 01K37BHY09HV9Y6H8VHCFNTQRN
3 01K37BHY09HV9Y6H8VHCFNTQRP