Id generator

Напомню, что id-generator имеет один метод generate, который возвращает положительное целое число:

(ns publicator.domain.abstractions.id-generator
  (:require
   [clojure.spec.alpha :as s]))

(defprotocol IdGenerator
  (-generate [this]))

(declare ^:dynamic *id-generator*)

(s/def ::id pos-int?)

(s/fdef generate
  :ret ::id)

(defn generate []
  (-generate *id-generator*))

В PostgreSQL для генерации идентификаторов используют sequence.

Создадим ее первой миграцией:

-- persistence/resources/db/migration/V1__id_sequence.sql
CREATE SEQUENCE "id-generator";

Генерируется новый идентификатор следующим запросом:

SELECT nextval('id-generator') AS id

Добавим реализацию протокола IdGenerator:

(ns publicator.persistence.id-generator
  (:require
   [jdbc.core :as jdbc]
   [publicator.domain.abstractions.id-generator :as id-generator]))

(deftype IdGenerator [data-source]
  id-generator/IdGenerator
  (-generate [_]
    (with-open [conn (jdbc/connection data-source)]
      (let [stmt (jdbc/prepared-statement conn "SELECT nextval('id-generator') AS id")
            resp (jdbc/fetch-one conn stmt)]
        (:id resp)))))

(defn binding-map [datasource]
  {#'id-generator/*id-generator* (->IdGenerator datasource)})

И его тест:

(ns publicator.persistence.id-generator-test
  (:require
   [clojure.test :as t]
   [publicator.domain.abstractions.id-generator :as id-generator]
   [publicator.utils.test.instrument :as instrument]
   [publicator.persistence.test.db :as db]
   [publicator.persistence.id-generator :as sut]))

(defn- setup [t]
  (with-bindings (sut/binding-map db/*data-source*)
    (t)))

(t/use-fixtures :once
  instrument/fixture
  db/once-fixture)

(t/use-fixtures :each
  db/each-fixture
  setup)

(t/deftest generate
  (t/is (pos-int? (id-generator/generate))))

Оптимизация

Если поштучная генерация идентификаторов станет проблемой, ее можно оптимизировать. Можно увеличивать счетчик не на 1, а сразу на 1000 и держать в памяти диапазон идентификаторов и при необходимости генерировать новый. Однако, вы потеряете хронологическую сортировку идентификаторов, если запущено более одного инстанса приложения.