yetanalytics / colossal-squuid Goto Github PK
View Code? Open in Web Editor NEWLibrary for generating sequential UUIDs, or SQUUIDs
Home Page: https://cljdoc.org/d/com.yetanalytics/colossal-squuid/0.1.4/doc/readme
License: Apache License 2.0
Library for generating sequential UUIDs, or SQUUIDs
Home Page: https://cljdoc.org/d/com.yetanalytics/colossal-squuid/0.1.4/doc/readme
License: Apache License 2.0
Hi,
and thanks for a great idea and library!
I believe there is a race condition in generate-squuid*
though.
Here is a way to reproduce this using CountDownLatch:
(defonce latch (atom nil))
(defn generate-squuid*
"Return a map containing the following:
:squuid The v8 sequential UUID made up of a base UUID and timestamp.
:base-uuid The base v4 UUID that provides the lower 80 bits.
:timestamp The timestamp that provides the higher 48 bits.
See `generate-squuid` for more details."
[]
(let [ts (java.time.Instant/ofEpochMilli 123) ; pretend we always get the same time
{curr-ts :timestamp} @current-time-atom]
(if (t/before? curr-ts ts)
;; No timestamp clash - make new UUIDs
(do
(.countDown @latch)
(.await @latch) ; wait for 2 threads to arrive here at the same time,
; i.e. after the compare is done.
(swap! current-time-atom (fn [m]
(-> m
(assoc :timestamp ts)
(merge (u/make-squuid ts))))))
;; Timestamp clash - increment UUIDs
(swap! current-time-atom (fn [m]
(-> m
(update :base-uuid u/inc-uuid)
(update :squuid u/inc-uuid)))))))
(comment
(do
(reset! current-time-atom {:timestamp t/zero-time
:base-uuid u/zero-uuid
:squuid u/zero-uuid})
(reset! latch (java.util.concurrent.CountDownLatch. 2))
(let [f1 (future (:squuid (generate-squuid*)))
f2 (future (:squuid (generate-squuid*)))
mx (last (sort [@f1 @f2]))]
; Now we have generated two squuids.
; None of them is thought to be "clashy" by the existing code.
; This is due to the race condition.
(let [new (:squuid (generate-squuid*)) ; new should always be higher than the max seen so far, right?
new-max (last (sort [mx new]))]
(if (not= new-max new)
(do
(println "new: " new)
(println "old max:" mx)
(println "bug"))
(do
(println "new: " new)
(println "old max:" mx)
(println "ok this time")))))))
Evaluate that comment form a few times, and you will (sooner or later) hit the race condition. Example output:
new: #uuid "00000000-007b-8715-82ac-485927e454a0"
old max: #uuid "00000000-007b-885f-acaa-e83df5e15fff"
bug
Thus this proves the race condition.
The correct way to handle this is to put (if (t/before? curr-ts ts)
inside the function sent to swap!
:
(swap! current-time-atom (fn [m]
(if (t/before? (:timestamp m) ts)
(-> m
(assoc :timestamp ts)
(merge (u/make-squuid ts)))
(-> m
(update :base-uuid u/inc-uuid)
(update :squuid u/inc-uuid)))))
Thus I'm proposing the following definition of generate-squuid*
:
(defn generate-squuid*
"Return a map containing the following:
:squuid The v8 sequential UUID made up of a base UUID and timestamp.
:base-uuid The base v4 UUID that provides the lower 80 bits.
:timestamp The timestamp that provides the higher 48 bits.
See `generate-squuid` for more details."
[]
(let [ts (t/current-time)]
(swap! current-time-atom (fn [m]
(if (t/before? (:timestamp m) ts)
(-> m
(assoc :timestamp ts)
(merge (u/make-squuid ts)))
(-> m
(update :base-uuid u/inc-uuid)
(update :squuid u/inc-uuid)))))))
I hope this makes sense.
Thanks and kind regards.
Edit: Added full proposed definition of generate-squuid*
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.