github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/protobuf/protoc-gen-go-grain/templates/grain.tmpl (about) 1 {{ $service := . }} 2 var x{{ $service.Name }}Factory func() {{ $service.Name }} 3 4 // {{ $service.Name }}Factory produces a {{ $service.Name }} 5 func {{ $service.Name }}Factory(factory func() {{ $service.Name }}) { 6 x{{ $service.Name }}Factory = factory 7 } 8 9 // Get{{ $service.Name }}GrainClient instantiates a new {{ $service.Name }}GrainClient with given Identity 10 func Get{{ $service.Name }}GrainClient(c *cluster.Cluster, id string) *{{ $service.Name }}GrainClient { 11 if c == nil { 12 panic(fmt.Errorf("nil cluster instance")) 13 } 14 if id == "" { 15 panic(fmt.Errorf("empty id")) 16 } 17 return &{{ $service.Name }}GrainClient{Identity: id, cluster: c} 18 } 19 20 // Get{{ $service.Name }}Kind instantiates a new cluster.Kind for {{ $service.Name }} 21 func Get{{ $service.Name }}Kind(opts ...actor.PropsOption) *cluster.Kind { 22 props := actor.PropsFromProducer(func() actor.Actor { 23 return &{{ $service.Name }}Actor{ 24 Timeout: 60 * time.Second, 25 } 26 }, opts...) 27 kind := cluster.NewKind("{{ $service.Name }}", props) 28 return kind 29 } 30 31 // Get{{ $service.Name }}Kind instantiates a new cluster.Kind for {{ $service.Name }} 32 func New{{ $service.Name }}Kind(factory func() {{ $service.Name }}, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { 33 x{{ $service.Name }}Factory = factory 34 props := actor.PropsFromProducer(func() actor.Actor { 35 return &{{ $service.Name }}Actor{ 36 Timeout: timeout, 37 } 38 }, opts...) 39 kind := cluster.NewKind("{{ $service.Name }}", props) 40 return kind 41 } 42 43 // {{ $service.Name }} interfaces the services available to the {{ $service.Name }} 44 type {{ $service.Name }} interface { 45 Init(ctx cluster.GrainContext) 46 Terminate(ctx cluster.GrainContext) 47 ReceiveDefault(ctx cluster.GrainContext) 48 {{- range $method := .Methods }} 49 {{ if $method.Options.Reenterable -}} 50 {{ $method.Name }}(req *{{ $method.Input }}, respond func(*{{ $method.Output }}), onError func(error), ctx cluster.GrainContext) error 51 {{- else -}} 52 {{ $method.Name }}(req *{{ $method.Input }}, ctx cluster.GrainContext) (*{{ $method.Output }}, error) 53 {{- end }} 54 {{- end}} 55 } 56 57 // {{ $service.Name }}GrainClient holds the base data for the {{ $service.Name }}Grain 58 type {{ $service.Name }}GrainClient struct { 59 Identity string 60 cluster *cluster.Cluster 61 } 62 {{ range $method := .Methods}} 63 {{ if $method.Options.Future -}} 64 // {{ $method.Name }}Future return a future for the execution of {{ $method.Name }} on the cluster 65 func (g *{{ $service.Name }}GrainClient) {{ $method.Name }}Future(r *{{ $method.Input }}, opts ...cluster.GrainCallOption) (*actor.Future, error) { 66 bytes, err := proto.Marshal(r) 67 if err != nil { 68 return nil, err 69 } 70 71 reqMsg := &cluster.GrainRequest{MethodIndex: {{ $method.Index }}, MessageData: bytes} 72 f, err := g.cluster.RequestFuture(g.Identity, "{{ $service.Name }}", reqMsg, opts...) 73 if err != nil { 74 return nil, fmt.Errorf("error request future: %w", err) 75 } 76 77 return f, nil 78 } 79 {{ end }} 80 // {{ $method.Name }} requests the execution on to the cluster with CallOptions 81 func (g *{{ $service.Name }}GrainClient) {{ $method.Name }}(r *{{ $method.Input }}, opts ...cluster.GrainCallOption) (*{{ $method.Output }}, error) { 82 bytes, err := proto.Marshal(r) 83 if err != nil { 84 return nil, err 85 } 86 reqMsg := &cluster.GrainRequest{MethodIndex: {{ $method.Index }}, MessageData: bytes} 87 resp, err := g.cluster.Request(g.Identity, "{{ $service.Name }}", reqMsg, opts...) 88 if err != nil { 89 return nil, fmt.Errorf("error request: %w", err) 90 } 91 switch msg := resp.(type) { 92 case *{{ $method.Output }}: 93 return msg, nil 94 case *cluster.GrainErrorResponse: 95 if msg == nil { 96 return nil, nil 97 } 98 return nil, msg 99 default: 100 return nil, fmt.Errorf("unknown response type %T", resp) 101 } 102 } 103 {{ end }} 104 // {{ $service.Name }}Actor represents the actor structure 105 type {{ $service.Name }}Actor struct { 106 ctx cluster.GrainContext 107 inner {{ $service.Name }} 108 Timeout time.Duration 109 } 110 111 // Receive ensures the lifecycle of the actor for the received message 112 func (a *{{ $service.Name }}Actor) Receive(ctx actor.Context) { 113 switch msg := ctx.Message().(type) { 114 case *actor.Started: //pass 115 case *cluster.ClusterInit: 116 a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) 117 a.inner = x{{ $service.Name }}Factory() 118 a.inner.Init(a.ctx) 119 120 if a.Timeout > 0 { 121 ctx.SetReceiveTimeout(a.Timeout) 122 } 123 case *actor.ReceiveTimeout: 124 ctx.Poison(ctx.Self()) 125 case *actor.Stopped: 126 a.inner.Terminate(a.ctx) 127 case actor.AutoReceiveMessage: // pass 128 case actor.SystemMessage: // pass 129 130 case *cluster.GrainRequest: 131 switch msg.MethodIndex { 132 {{ range $method := .Methods -}} 133 case {{ $method.Index }}: 134 req := &{{ $method.Input }}{} 135 err := proto.Unmarshal(msg.MessageData, req) 136 if err != nil { 137 ctx.Logger().Error("[Grain] {{ $method.Name }}({{ $method.Input }}) proto.Unmarshal failed.", slog.Any("error", err)) 138 resp := cluster.NewGrainErrorResponse(cluster.ErrorReason_INVALID_ARGUMENT, err.Error()). 139 WithMetadata(map[string]string{ 140 "argument": req.String(), 141 }) 142 ctx.Respond(resp) 143 return 144 } 145 {{ if $method.Options.Reenterable -}} 146 err = a.inner.{{ $method.Name }}(req, respond[*{{ $method.Output }}](a.ctx), a.onError, a.ctx) 147 {{ else }} 148 r0, err := a.inner.{{ $method.Name }}(req, a.ctx) 149 {{ end -}} 150 if err != nil { 151 resp := cluster.FromError(err) 152 ctx.Respond(resp) 153 return 154 } 155 {{ if not $method.Options.Reenterable -}} 156 ctx.Respond(r0) 157 {{ end -}} 158 {{ end -}} 159 } 160 default: 161 a.inner.ReceiveDefault(a.ctx) 162 } 163 } 164 165 // onError should be used in ctx.ReenterAfter 166 // you can just return error in reenterable method for other errors 167 func (a *{{ $service.Name }}Actor) onError(err error) { 168 resp := cluster.FromError(err) 169 a.ctx.Respond(resp) 170 }