storm icon indicating copy to clipboard operation
storm copied to clipboard

Add support for ULID

Open bep opened this issue 7 years ago • 11 comments

See https://github.com/oklog/ulid

This ID has some interesting characteristics that would be a good fit for Storm, me thinks, one of them being the "lexicographically sortable".

It should live in a separate package, I guess, but I think it would be useful as a built-in option similar to AutoIncrement -- `AutoULID``?

If there this sound interesting I can take a stab on implementing it.

bep avatar Dec 06 '16 18:12 bep

Thinking about it a little, one of the plus sides of these IDs is that they can be set before persisted to DB, so if this is added we should add a way to just get a new ID -- still valuable as me and others then do not have to set up pooling for rand etc.

bep avatar Dec 06 '16 19:12 bep

I think there should be a way to setup the incrementer that would be called by Storm everytime it needs a new ID.

asdine avatar Dec 08 '16 16:12 asdine

I think there should be a way to setup the incrementer that would be called by Storm everytime it needs a new ID.

Yes, we should add some kind of IDProvider interface, which defaults to the current

So we could do something ala:

db, cleanup := createDB(b, AutoIncrement(ULID()))

Or something.

And then add a GetIDProvider() func, which in some cases would fail if not inside a transaction.

The AutoIncrement setting name is unfortunate, maybe IDProvider... I see the GoDoc is still talking about BoltDB's NextSequence, but it seems to have been replaced with some custom; what is the reason behind that @asdine?

bep avatar Jun 05 '17 18:06 bep

If i remember well, it was because NextSequence doesn't allow us to set the initial value of the counter. Also, the AutoIncrement option is deprecated and should be removed soon.

Concerning the IDProvider it seems to be a good idea.

Currently, there is a metadata bucket stored inside each bucket that contains the codec used and the current counter. It could be used to call the IDProvider to generate the next id by passing the last one used.

db, err := storm.Open("my.db", IDProvider(ULID())) 
type IDProvider interface {
  NextID(last interface{}) interface{}
}

I'm not a big fan of empty interfaces but i don't think we have a choice here wdyt @bep ?

asdine avatar Jun 07 '17 14:06 asdine

Would it not be be better if it was non race condition exposed ? In other words ask the metaBucket / IDProvider for the next ID, because only it knows.

Or

After a commit just always send back the ID in the return call. Then there is no chance of race condition.

joeblew99 avatar Jun 07 '17 14:06 joeblew99

@asdine IDProvider can't be both this interface, and a function that returns func(*DB) error, per:

func Open(path string, stormOptions ...func(*DB) error) (*DB, error)

I envisioned something like:

type IDProviderFunc func(last interface{}) interface{}

func IDProvider(IDProviderFunc) func(*DB) error {...}
db, err := storm.Open("my.db", IDProvider(storm.ULID)) 

I'm not a huge fan of the empty interfaces here either, but alternatives do not come to mind immediately.

@joeblew99 this is why @bep suggests that some cases would error outside a transaction, otherwise, a mutex per bucket is probably required, but all this would be transparent to the user/provider-implementer.

pdf avatar Jun 07 '17 15:06 pdf

@joeblew99 Actually, the meta.Increment call is done within a write transaction so it's not subject to race conditions. Also the call to the IDProviderFunc will be done exclusively inside the write transaction.

@pdf Yes sorry, i picked the wrong name and my example is confusing. Your solution is cool, a function should be enough.

asdine avatar Jun 07 '17 15:06 asdine

type IDProviderFunc func(last interface{}) interface{}

What is the purpose of last? I thought this was either very stateful (meta.Increment) or very stateless (ULID, UUID...).

bep avatar Jun 07 '17 15:06 bep

This supports both stateful and stateless - last would be the previous int for auto-increment, and ignored by the ULID implementation.

pdf avatar Jun 07 '17 15:06 pdf

Right, so declaring the IDProvider attaches it to the DB instance, the meta bucket stores the last ID, which is passed to the attached provider, which returns the next ID. This method of declaration allows people to implement external providers if they so choose, but we would have an auto-increment and ULID provider available inside storm.

Make sense?

pdf avatar Jun 07 '17 15:06 pdf

Also see https://github.com/segmentio/ksuid

bep avatar Jun 08 '17 11:06 bep