Search code examples
gohashprotobuf-go

Hash protobuf object to a string as the key for redis database


I have some sort of complex protobuf object. It's a request sent to my GRPC endpoint. I want to just process it if I didn't before. So I want to hash the object to some string and store it in my Redis database. I used ObjectHash-Proto but with new versions of protobuf-compiler I got this error:

got an unexpected struct of type 'impl.MessageState' for field {Name:state PkgPath:... Type:impl.MessageState Tag: Offset:0 Index:[0] Anonymous:false}

Seems it does not support structs and new version of protobuf-compiler generates a code containing struct.

I can't generate some sort of ID for each request. The ID is actually the hash of the whole of the object.


Solution

  • If you have a proto.Message [1], then you get a Marshal function [2] for free. So after you Marshal the message, you can just pass the bytes to base64, or md5 or whatever you want:

    package main
    
    import (
       "encoding/base64"
       "google.golang.org/protobuf/proto"
       "google.golang.org/protobuf/types/known/structpb"
    )
    
    func hash(m proto.Message) (string, error) {
       b, err := proto.Marshal(m)
       if err != nil {
          return "", err
       }
       return base64.StdEncoding.EncodeToString(b), nil
    }
    
    func main() {
       m, err := structpb.NewStruct(map[string]interface{}{
          "month": 12, "day": 31,
       })
       if err != nil {
          panic(err)
       }
       s, err := hash(m)
       if err != nil {
          panic(err)
       }
       println(s) // ChIKBW1vbnRoEgkRAAAAAAAAKEAKEAoDZGF5EgkRAAAAAAAAP0A=
    }
    
    1. https://godocs.io/google.golang.org/protobuf/proto#Message
    2. https://godocs.io/google.golang.org/protobuf/proto#Marshal