{"action":"created","issue":{"url":"https://api.github.com/repos/alizain/ulid/issues/3","repository_url":"https://api.github.com/repos/alizain/ulid","labels_url":"https://api.github.com/repos/alizain/ulid/issues/3/labels{/name}","comments_url":"https://api.github.com/repos/alizain/ulid/issues/3/comments","events_url":"https://api.github.com/repos/alizain/ulid/issues/3/events","html_url":"https://github.com/alizain/ulid/issues/3","id":169268525,"number":3,"title":"Not sure I like the new spec's \"increment within same ms\" change + more thoughts","user":{"login":"RobThree","id":607888,"avatar_url":"https://avatars.githubusercontent.com/u/607888?v=3","gravatar_id":"","url":"https://api.github.com/users/RobThree","html_url":"https://github.com/RobThree","followers_url":"https://api.github.com/users/RobThree/followers","following_url":"https://api.github.com/users/RobThree/following{/other_user}","gists_url":"https://api.github.com/users/RobThree/gists{/gist_id}","starred_url":"https://api.github.com/users/RobThree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RobThree/subscriptions","organizations_url":"https://api.github.com/users/RobThree/orgs","repos_url":"https://api.github.com/users/RobThree/repos","events_url":"https://api.github.com/users/RobThree/events{/privacy}","received_events_url":"https://api.github.com/users/RobThree/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-08-04T01:02:21Z","updated_at":"2016-08-04T22:32:42Z","closed_at":null,"body":"You [changed the spec](https://github.com/alizain/ulid/commit/9f01c3ed1d4fdf6acfddb6d9f9cf49e621fa5de9#diff-04c6e90faac2675aa89e2176d2eec7d8R89) to include:\r\n\r\n> To ensure sortability for IDs generated within the same millisecond, implementations should remember the most recently generated randomness string and increment the most significant character that is not the last character in the encoding.\r\n\r\nI don't think you understand the impact of this change; it brings a *whole lot of hurt* into your world. For instance, you can't rely on wall-clock-time (which [you fell for](https://github.com/alizain/ulid/blob/9f01c3ed1d4fdf6acfddb6d9f9cf49e621fa5de9/index.js#L83)). Unless you can get a **reliable, monotonic, strictly increasing** timesource (which is (near?) impossible AFAIK in JS) you'll run into trouble later. Why? Because: [wall-clock-time goes back (DST) / stands still (leap seconds are sometimes implemented as such) / does other weird stuff](http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time) (and [there's even more](http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time)). Trust me, [I know](https://github.com/RobThree/IdGen) :wink: Sidenote: some systems may not even have 'ms-resolution' timers, something to consider too.\r\n\r\nThere's 80 bits of random data; I think it's safe to assume (even with the [birthday paradox](https://en.wikipedia.org/wiki/Birthday_problem) in mind) that the chance on a collision (within any, random, millisecond) is similar to the chance of a GUID collision. However, that would be if the ULID's would be spread evenly across time (which would, in effect, make a ULID similar or even equal to a GUID). So, given that ULID's are typically 'clustered' in a certain timespan the chance on a collision goes up. Worst-case would be a DST change + some other 'random event' that would cause the same 'millisecond'-point-in-time to exist (say) 3 or 4 times. The chance on a collision would increase a lot but (I haven't done the math yet) would still suffice for most purposes unless you're Google maybe and generating many millions or even billions of ULID's p/ms.\r\n\r\nSo I guess my advice is to leave that part out of the spec (and, let's be honest, [this method](https://github.com/alizain/ulid/blob/master/index.js#L64) is no beauty either (it could be improved but wouldn't be worth the trouble IMHO))  and keep implementations *much* simpler that way. You could compare a ULID it with a [v4 UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29) with the only difference that the first 6 bytes (48 bits) are reserved for a timestamp for better ordering. However, it *does* mean that, within the millisecond, the ordering is 'random' so that the spec may need to be more clear about it / reflect that fact.\r\n\r\nBesides time, by the way, this tiny change in the spec also requires you to keep state between generating ULID's (so you'll need to keep the generator-object around for example), introduces chances for race conditions (multithreading; N threads generating ULID's will, without proper locking etc. inevitably generate exactly the same ULID's) and all other sorts of things that _either_ need to be taken for granted (which will bite you in the \\*ss sooner than later) *or* need to be worked out / specified.\r\n\r\nFor inspiration about some more of the problems you might run into you might want to look at Twitter's [Snowflake](https://github.com/twitter/snowflake) (retired but [still available](https://github.com/twitter/snowflake/tree/b3f6a3c6ca8e1b6847baa6ff42bf72201e2c2231)) or have a look at my \"Snowflake-alike\" [IdGen](https://github.com/RobThree/IdGen). Both are 64 bit (actually 63\\*) equivalents of ulid.\r\n\r\n\\* Come to think of it: You might also want to steer clear of the sign-bit; when systems would order on the (internal) 128 bit representation / byte-array (for 'speed') the order may 'break' as soon as the sign bit is set to `1`. Twitter thought of it *way* before it happened but I think it's a good idea. Sacrificing 1 bit out of 128 should't be *that* much of a problem (though it does halve your \"ulid-space\" from 2^128 to 2^127).\r\n\r\nI was/am doing a ulid .Net port (waaay more extensive than [fvilvers'](https://github.com/fvilers/ulid.net), with all due respect!) and also implemented the binary form, created methods to convert to/from UUID's (since ULID's and UUID's are both 128 bit you can easily convert one to the other and vice versa). I also implemented a `Parse(...)` method and some others and ~~will publish on GitHub very soon~~ [have put it on GitHub](https://github.com/RobThree/NUlid). There are some things I thought of / came across which might need 'agreement' and be put in the spec:\r\n\r\n- ~~ulid's are, IMHO, case-**in**sensitive (e.g. **NOT** case-sensitive)~~ We seem to agree on that; missed it in [your README](https://github.com/alizain/ulid/blob/master/README.md) :stuck_out_tongue: \r\n- We may want to allow / think about / specify a(n optional?) separator for readability. No `{` and `}` mess like (G/U)UID's but something like `ttttt-ttttt-rrrrrr-rrrrr-rrrrr` (lengths 5-5-6-5-5) might make ulid's more readable. Other formats/separators are ofcourse a possibility (as well as none of this ofcourse; I'm just bringing my ideas to the table)\r\n- You/we may want to decide on pronunciation and document it: \"you-lid\"? \"you-el-i-dee\"? \"uhl-id\"? Something else? You/we want to avoid the GIF's [\"Jif\" v.s. \"Gif\"](http://howtoreallypronouncegif.com/) wars\r\n- Besides the pronunciation, same goes for a \"standardized\" writing. I catch myself writing `ulid`, `ULID`, `UlId`, ... so that may have to be standardized too\r\n- I had more... but can't think of it now. Will add here / open new issue when I think of it.\r\n\r\nSo far, for now, [another $0.02 of mine](https://github.com/alizain/ulid/issues/2) :stuck_out_tongue_closed_eyes: \r\n\r\n---\r\n\r\n**Edit:** FYI; I just released [V1.0 of NUlid](https://github.com/RobThree/NUlid). :tada: Maybe you can add it to [your list of community contributed ports](https://github.com/alizain/ulid/blob/master/README.md#implementations-in-other-languages)?"},"comment":{"url":"https://api.github.com/repos/alizain/ulid/issues/comments/237703446","html_url":"https://github.com/alizain/ulid/issues/3#issuecomment-237703446","issue_url":"https://api.github.com/repos/alizain/ulid/issues/3","id":237703446,"user":{"login":"waaghals","id":458580,"avatar_url":"https://avatars.githubusercontent.com/u/458580?v=3","gravatar_id":"","url":"https://api.github.com/users/waaghals","html_url":"https://github.com/waaghals","followers_url":"https://api.github.com/users/waaghals/followers","following_url":"https://api.github.com/users/waaghals/following{/other_user}","gists_url":"https://api.github.com/users/waaghals/gists{/gist_id}","starred_url":"https://api.github.com/users/waaghals/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/waaghals/subscriptions","organizations_url":"https://api.github.com/users/waaghals/orgs","repos_url":"https://api.github.com/users/waaghals/repos","events_url":"https://api.github.com/users/waaghals/events{/privacy}","received_events_url":"https://api.github.com/users/waaghals/received_events","type":"User","site_admin":false},"created_at":"2016-08-04T22:32:42Z","updated_at":"2016-08-04T22:32:42Z","body":"Firstly, Dôh. I've just spent the last two hour or so implementing a proper port to .Net not knowing one already existed.  I pushed what I have, but I'll just abandon mine because yours is much more complete.\r\n\r\nI'm unsure about leaving out the 'increment within same ms' as this will guaranty that the id's are always monotonically increasing. However I was thinking of creating a generator for this instead of implementing it in the Ulid Type itself so it does not have that responsibility. This way it could be left out of the spec, while still be available for consumers which need this guaranty.\r\n\r\nThe race conditions are solvable within C#, but I'm not so confident for Javascript because it does not have a notion of locking. However [Firebase](https://gist.github.com/mikelehen/3596a30bd69384624c11) has been successfully using a JavaScript approach for some time now.\r\n\r\n> You/we may want to decide on pronunciation and document it: \"you-lid\"? \"you-el-i-dee\"? \"uhl-id\"? \r\n\r\nTo late, I already started calling the \"you-lid\" :stuck_out_tongue_winking_eye: "}}
{"actor":{"display_login":"waaghals"}}