protoc-gen-go-hash is a simple compiler plugin to protoc. It adds one method called Hash to the message struct. This is
used to produce a deterministic hash out of the protocol buffer message.
At Refurbed we have a caching system that supports A/B testing. Therefore, we have the need to cache protocol buffer messages. We cannot really get a hash of the actual protobuf binary data since the default serialization is not deterministic, neither can we hash the json marshalled object because objects are unordered collections of name/value pairs. So we are left with using hashstructure.
In order to install the protoc-gen-go-hash plugin to use it in your projects first run:
go install github.com/refurbed/protoc-gen-go-hashThen import hashstructure dependency in your project:
go get github.com/mitchellh/hashstructure/v2And finally generate the protobuf messages you require:
protoc --go_out=. --go-hash_out=. <your proto files>First clone the repo.
Then build the protoc-gen-go-hash plugin:
go build ./cmd/protoc-gen-go-hashAnd finally generate the example protobuf message:
protoc --plugin=protoc-gen-go-hash=./protoc-gen-go-hash --go_out=. --go_opt=paths=source_relative --go-hash_out=. --go-hash_opt=paths=source_relative example/example.protoFor the example protobuf file, the following two files will be created, example.pb.go and example_hash.pb.go:
proto-gen-hash/
├─ example/
│ ├─ example.proto
│ ├─ example.pb.go
│ ├─ example_hash.pb.go
example.pb.go is the familiar code generated by the protoc go plugin. And then the example_hash.pb.go will look like this:
// Code generated by protoc-gen-go-hash. DO NOT EDIT.
package example
import (
"fmt"
"github.com/mitchellh/hashstructure/v2"
)
func (x *Hello) Hash() (uint64, error) {
if x == nil {
return 0, fmt.Errorf("message is defined as nil")
}
return hashstructure.Hash(*x, hashstructure.FormatV2, nil)
}Therefore, the following code:
h := &example.Hello{}
h.CustomerId = "1234"
fmt.Println(h.Hash())prints out:
10958536349413352795 <nil>