Skip to content

Instantly share code, notes, and snippets.

@turtlemonvh
Created March 4, 2016 20:10
Show Gist options
  • Save turtlemonvh/a3fae7e1b22b03ce63db to your computer and use it in GitHub Desktop.
Save turtlemonvh/a3fae7e1b22b03ce63db to your computer and use it in GitHub Desktop.
Example of shifting the time components of a bson object id from mgo

Go retime objectid experiment

The mgo library has a function for generating an id with a timestamp (https://godoc.org/labix.org/v2/mgo/bson#NewObjectIdWithTime), but all the non-time related fields are zeroed out. This is useful for time range comparisons, but kind of annoying if you want to generate a new ObjectId for a particular time range that may be in the past or future.

This example shows how to take an existing unique ObjectId and adjust its origin time.

Example output

timothy$ go run bson_retime_experiment.go
ObjectId0: 56d9e9a32eda1c31ed89b862 	time: 2016-03-04 15:01:39 -0500 EST
ObjectId1: 56d9e9a52eda1c31ed89b862 	time: 2016-03-04 15:01:41 -0500 EST

Warning!!!

The bson library does a lot to prevent id collisions (see: https://github.com/go-mgo/mgo/blob/r2016.02.04/bson/bson.go#L228). This includes

  • including a hash of the hostname
  • including a hash of the process id
  • maintaining an incrementor, to ensure that when multiple ids are created in a second there is no collision

The last piece (the autoincrementing id) is a protection you are forgoing by choosing to use this technique. Unless all your ids are shifted back the same amount every time (which is very unlikely) you have no guarentees that by shifting the time component of an id you won't just shift it right on top of another id. And id collisions aren't good for anyone.

package main
import (
"encoding/binary"
"fmt"
"gopkg.in/mgo.v2/bson"
"time"
)
func retimeObjectId(id bson.ObjectId, t time.Time) bson.ObjectId {
bts := []byte(id)
binary.BigEndian.PutUint32(bts[:4], uint32(t.Unix()))
return bson.ObjectId(bts)
}
func main() {
id0 := bson.NewObjectId()
time.Sleep(2 * time.Second)
id1 := retimeObjectId(id0, time.Now())
fmt.Printf("ObjectId0: %s \ttime: %s\n", id0.Hex(), id0.Time())
fmt.Printf("ObjectId1: %s \ttime: %s\n", id1.Hex(), id1.Time())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment