bilal-fazlani / zio-ulid

zio implementation for https://github.com/ulid/spec
https://zio-ulid.bilal-fazlani.com
MIT License
12 stars 2 forks source link

Add nextULIDFromEpoch #6

Open dacr opened 11 months ago

dacr commented 11 months ago

It would be great to add such method in your ULID service :

def nextULIDFromEpoch(epochMillis: Long): URIO[ULIDGen, ULID] = ???

This is a common need to generate an ULID from a given timestamp, this is what I'm doing for example in a project dedicated to photos management :

  import wvlet.airframe.ulid.ULID

  def buildPhotoId(timestamp: OffsetDateTime): PhotoId = {
    val ulid = ULID.ofMillis(timestamp.toInstant.toEpochMilli)
    PhotoId(ulid)
  }

And I've to admit that's the reason why, I use directly wvlet.airframe.ulid instead of zio-ulid, for the moment.

bilal-fazlani commented 11 months ago

Hmmm... interesting requirement

Out of curiosity, instead of creating a timestamp first and then generating a ULID, why don't you generate a ULID first. And then use its timestamp method? Is it because you are reading timestamp from the photos?

Anyways, if we want to implement nextULIDFromEpoch method , how should it behave when someone calls it too many times in the same millisecond? We can't stick to existing behaviour of waiting for 1 millisecond and returning a new ULID with new timestamp. Timestamp is locked in our case. So we may have to return a new error: RandomOverflowError 🤔

bilal-fazlani commented 11 months ago

I looked at airframe implementation for reference.

Untitled_Artwork

In case of overflow, airframe waits for 1 millisecond and generates another ULID. But the new ULID does not simply increment 1 millisecond. It creates a new ULID ignoring the given epoch milliseconds. It then uses current system timestamp.

image

Although chances are thin, that can be very surprising and buggy behaviour.