Git Product home page Git Product logo

colossal-squuid's People

Contributors

blakeplock avatar kelvinqian00 avatar zeitstein avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

colossal-squuid's Issues

Strict monotonicity not always held / race condition in generate-squuid*

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*.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.