Является ли это безопасным способом одновременного использования ulid с другими библиотеками?

Я пытаюсь использовать в первый раз пакет ulid .

В своем README они говорят:

Обратите внимание, чтоrand.Randизmathpackage небезопасен для одновременного использования. Создайте один экземпляр для каждой долгоживущей процедуры или используйтеsync.Poolесли вы хотите избежать потенциальной конкуренции заблокированногоrand.Sourceкак это часто наблюдается в функциях уровня пакета.

Можете ли вы помочь мне понять, что это значит и как написать БЕЗОПАСНЫЙ код для одновременного использования с такими библиотеками, как ent или gqlgen?

Пример: я использую приведенный ниже код в своем приложении для создания новых идентификаторов (иногда даже многих из них за одну миллисекунду, что подходит дляulid).

      import (
  "math/rand"
  "time"

  "github.com/oklog/ulid/v2"
)

var defaultEntropySource *ulid.MonotonicEntropy

func init() {
  defaultEntropySource = ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 0)
}

func NewID() string {
  return ulid.MustNew(ulid.Timestamp(time.Now()), defaultEntropySource).String()
}

Это безопасный способ использования пакета?

1 ответ

Это безопасный способ использования пакета?

Нет, это предложение предполагает, что каждый rand.Source должен быть локальным для горутины, вашейdefaultEntropySourcerand.Source часть потенциально может быть разделена между несколькими горутинами.

Как описано в New function, вам нужно только убедиться, что читатель энтропии безопасен для одновременного использования, а Monotonic — нет. Вот два способа реализации предложения документации:

Создайте один rand.Source для каждого вызова NewID(), выделяет новую энтропию для каждого вызова NewID

      func NewID() string {
    defaultEntropySource := ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 0)
    return ulid.MustNew(ulid.Timestamp(time.Now()), defaultEntropySource).String()
}

Детская площадка

Как и выше, но с использованием sync.Pool для возможного повторного использования ранее выделенногоrand.Sourceс

      var entropyPool = sync.Pool{
    New: func() any {
        entropy := ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 0)
        return entropy
    },
}

func NewID() string {
    e := entropyPool.Get().(*ulid.MonotonicEntropy)
    s := ulid.MustNew(ulid.Timestamp(time.Now()), e).String()
    entropyPool.Put(e)
    return s
}

Детская площадка

Другие вопросы по тегам