At times snowflake id generation would create org and bucket IDs with
characters that had special meaning for the storage engine.
The storage engine concats the org and bucket bytes together into a
single 128 bit value. That value is used in the old measurement
section. Measurement was transformed into the tag, _measurement.
However, certain properties of the older measurement data location
are still required for the org/bucket bytes. We cannot have
commas, spaces, nor backslashes.
This PR puts a specific ID generator in place during the creation of
orgs and buckets. The IDs are just random numbers but with each
of the restricted chars incremented by one. While this changes the
entropy distribution somewhat, it does not matter too much for our
purposes.
... because now org and bucket ids are checked for previous existence
transactionally in the key-value stores. If the ID does already exist
then we try to generate a new key up to 100 times.
This can be done by embedding a httprouter.Router
(a light weight HTTP router that supports variables in the routing pattern and matches against the request method)
Required services should be exported on the struct
// ThingHandler represents an HTTP API handler for things.
typeThingHandlerstruct{// embedded httprouter.Router as a lazy way to implement http.Handler
*httprouter.RouterThingServiceplatform.ThingServiceAuthorizationServiceplatform.AuthorizationServiceLogger*zap.Logger}
HTTP Handler Constructor
Routes should be declared in the constructor
// NewThingHandler returns a new instance of ThingHandler.
funcNewThingHandler()*ThingHandler{h:=&ThingHandler{Router:httprouter.New(),Logger:zap.Nop(),}h.HandlerFunc("POST","/api/v2/things",h.handlePostThing)h.HandlerFunc("GET","/api/v2/things",h.handleGetThings)returnh}
Route handlers (http.HandlerFuncs)
Each route handler should have an associated request struct and decode function
The decode function should take a context.Context and an *http.Request and return the associated route request struct
Route http.HandlerFuncs should separate the decoding and encoding of HTTP requests/response from actual handler logic
// handlePostThing is the HTTP handler for the POST /api/v2/things route.
func(h*ThingHandler)handlePostThing(whttp.ResponseWriter,r*http.Request){ctx:=r.Context()req,err:=decodePostThingRequest(ctx,r)iferr!=nil{EncodeError(ctx,err,w)return}// Do stuff here
iferr:=h.ThingService.CreateThing(ctx,req.Thing);err!=nil{EncodeError(ctx,err,w)return}iferr:=encodeResponse(ctx,w,http.StatusCreated,req.Thing);err!=nil{h.Logger.Info("encoding response failed",zap.Error(err))return}}
http.HandlerFunc's that require particular encoding of http responses should implement an encode response function