Skip to content
This repository has been archived by the owner on Mar 11, 2022. It is now read-only.

Go support for CloudState User Functions #5

Merged
merged 13 commits into from
Oct 22, 2019

Conversation

marcellanz
Copy link
Contributor

@marcellanz marcellanz commented Oct 11, 2019

This PR implements Go support as a CloudState API Client Library and should fix cloudstateio/cloudstate#3 to a good amount.

Included is

  • The implementation of the Discovery and EventSourced Services of CloudState.
  • The implementation of the CloudState Serialization Convention.
  • The implementation of the TCK Shopping Cart example, used to be verified by the TCK.
  • Build support for travis providing a docker image so that the CloudState TCK can be run without the requirement to build the Go Shopping Cart by itself. Successful run here.
  • Paradox Documentation is provided separately in the main repository; see PR#128.

Before this separate repository was created, a previous Draft-PR was opened and reviewed partly.

- handleCommand cleanup
- added codecov to travis build
- refined snapshot handling
- snapshot support without tests
- implemented protobuf any serialization by CloudState conventions
- added benchmarks for CloudState serialization
- cleaned up documentation
- travis build for other OS (win,linux, macOS)
- refactored EventSourcedHandler.handleCommand
- aligned and cleaned up failure handling
@codecov-io
Copy link

codecov-io commented Oct 12, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@081e913). Click here to learn what that means.
The diff coverage is 51.78%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master       #5   +/-   ##
=========================================
  Coverage          ?   51.78%           
=========================================
  Files             ?        9           
  Lines             ?      645           
  Branches          ?        0           
=========================================
  Hits              ?      334           
  Misses            ?      255           
  Partials          ?       56
Impacted Files Coverage Δ
cloudstate/eventsourced_reply.go 10.93% <10.93%> (ø)
cloudstate/entity_key.pb.go 100% <100%> (ø)
cloudstate/cloudstate.go 43.69% <43.69%> (ø)
cloudstate/protosupport.go 50% <50%> (ø)
cloudstate/eventsourced.go 52.04% <52.04%> (ø)
cloudstate/encoding/any_json.go 62.96% <62.96%> (ø)
cloudstate/encoding/any_primitive.go 68.57% <68.57%> (ø)
cloudstate/marshal_proto.go 71.42% <71.42%> (ø)
cloudstate/event.go 88% <88%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 081e913...accb0f3. Read the comment docs.

Copy link

@jsravn jsravn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First review pass :). There's a lot here so will take another pass tomorrow. In addition to my comments I had a few other general thoughts:

  1. It would be nice if we can run the TCK in this repo so we get immediate feedback from changes whether we broke anything. But not sure how feasible that is, at least to do now.
  2. Need to think how we handle the go "logging problem" where we need to give some level of control to the user over logging.
  3. I think the API needs some improvements which is where my first pass was focused on but I didn't quite get to everything.

Bear with me as I'm also fairly new to cloudstate, so I'm learning the protocol as well as I go.

.travis.yml Show resolved Hide resolved
.travis.yml Outdated Show resolved Hide resolved
build/fetch-cloudstate-pb.sh Outdated Show resolved Hide resolved
.travis.yml Show resolved Hide resolved
build/TCK.Dockerfile Outdated Show resolved Hide resolved
cloudstate/cloudstate.go Show resolved Hide resolved
cloudstate/cloudstate.go Show resolved Hide resolved
persistenceID = e.PersistenceID
}
r.entitySpec.Entities = append(r.entitySpec.Entities, &protocol.Entity{
EntityType: EventSourced,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to get the value of EventSourced from the service descriptor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can't. Like for the other comments about a type safe way to get the service descriptor, we can't (see below the example for _EventSourced_serviceDesc). I explained this also in the then Draft - PR: cloudstateio/cloudstate#103 (comment) on how to access this information. The comment also mentions a few partly open Issues on the gRPC project for Go.

event_sourced.pb.go

var _EventSourced_serviceDesc = grpc.ServiceDesc{
	ServiceName: "cloudstate.eventsourced.EventSourced",
	HandlerType: (*EventSourcedServer)(nil),
	Methods:     []grpc.MethodDesc{},
	Streams: []grpc.StreamDesc{
		{
			StreamName:    "handle",
			Handler:       _EventSourced_Handle_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
	},
	Metadata: "cloudstate/event_sourced.proto",
}

@jsravn do you see any other way to do that? Tho, I've seen the https://github.com/gogo/protobuf project and that it is more open in this regard, but I have not tried it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think gogo is only protobuf 2? It's been a while since I looked at it.

It does seem like golang protobuf is fairly anemic, unfortunately.

But I think we can do this with protoreflect:

func (r *EntityDiscoveryService) registerEntity(e *EventSourcedEntity, config DescriptorConfig) error {
	if err := r.resolveFileDescriptors(config); err != nil {
		return fmt.Errorf("failed to resolveFileDescriptor for DescriptorConfig: %+v: %w", config, err)
	}
	persistenceID := e.entityName
	if e.PersistenceID != "" {
		persistenceID = e.PersistenceID
	}
	fd, err := desc.LoadFileDescriptor("cloudstate/event_sourced.proto")
	if err != nil {
		return err
	}
	r.entitySpec.Entities = append(r.entitySpec.Entities, &protocol.Entity{
		EntityType:    fd.FindService("EventSourced").GetFullyQualifiedName(),
		ServiceName:   e.ServiceName,
		PersistenceId: persistenceID,
	})
	return r.updateSpec()
}

cloudstate/eventsourced.go Show resolved Hide resolved
cloudstate/eventsourced.go Outdated Show resolved Hide resolved
@jroper jroper closed this Oct 14, 2019
@jroper jroper reopened this Oct 14, 2019
@jroper
Copy link
Member

jroper commented Oct 14, 2019

Sorry about the close/reopen noise, was just testing the CLA checker integration.

@jroper
Copy link
Member

jroper commented Oct 14, 2019

@marcellanz does it make sense to enable codecov for this repo? I've never enabled it before, if you could point me to instructions that would be helpful.

@marcellanz
Copy link
Contributor Author

marcellanz commented Oct 14, 2019

@jroper I haven't used it before and configured the simplest way I found through their bash uploader https://docs.codecov.io/v4.3.0/docs/about-the-codecov-bash-uploader

which does not need a token configured because of the public repo. I simply used their proposed integration
https://github.com/codecov/example-go and it seems to comment pull request which I didn't knew before. This can be configured by a *.yml file, but I haven't done that yet.

I added the badge to the README.md and my intention was to have just this badge together with two others there (godoc, travis build). I think the comment on the pull request is not needed, but having test coverage stats on a PR seems to be nice to have, even if its coverage at the moment is not that great.

So in this regard, I think you don't have to configure anything for the repo.

@jroper
Copy link
Member

jroper commented Oct 14, 2019

No worries - note though that we could have everything more automatic, with webhooks and commit statuses, if desired.

@marcellanz
Copy link
Contributor Author

First review pass :). There's a lot here so will take another pass tomorrow.

@jsravn Thanks a lot James for this thorough review of the PR, I appreciate it lot. I'm away this week but I'm on the contributors call today if we might want to discuss certain issues. I think especially of the "logging problem" where we could define how to do it for language support libraries in general. I'm looking forward to your 2nd pass :-)

@marcellanz
Copy link
Contributor Author

No worries - note though that we could have everything more automatic, with webhooks and commit statuses, if desired.

@jroper all right, let me know if we should have a closer look to codecov where it helps and how to integrate it best.

@jsravn
Copy link

jsravn commented Oct 16, 2019

@jsravn Thanks a lot James for this thorough review of the PR, I appreciate it lot. I'm away this week but I'm on the contributors call today if we might want to discuss certain issues. I think especially of the "logging problem" where we could define how to do it for language support libraries in general. I'm looking forward to your 2nd pass :-)

Hi @marcellanz, no problem :). I'd like to get this PR merged soon so we can collaborate more easily. The logging can probably be done in another PR - this PR doesn't need to be perfect. I was unable to make the call yesterday, but I will try to make next week.

@marcellanz
Copy link
Contributor Author

@jsravn Thanks a lot James for this thorough review of the PR, I appreciate it lot. I'm away this week but I'm on the contributors call today if we might want to discuss certain issues. I think especially of the "logging problem" where we could define how to do it for language support libraries in general. I'm looking forward to your 2nd pass :-)

Hi @marcellanz, no problem :). I'd like to get this PR merged soon so we can collaborate more easily. The logging can probably be done in another PR - this PR doesn't need to be perfect. I was unable to make the call yesterday, but I will try to make next week.

@jsravn Great, to get up-to-date with the calls, there are minutes available too https://docs.google.com/document/d/18_gS04R2ELAVmatW2nN5iR5PRmYqzQDbtQeS41oOIys
where latest stuff and next actions are summarized.

Copy link

@jsravn jsravn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I finished my second pass and so I think I'm mostly done w/ my review.

I think my main concern on this pass is the implicitness of the API. There is a lot of runtime stuff going on (reflection finding of methods by convention, runtime casts to interfaces), that doesn't feel like idiomatic go to me. I think we should err on the side of explicitness - right now it's not very obvious "what to implement" on the event sourced entity.

I'd like to see a single interface with the required handler methods on it that the EventSourcedEntity needs to implement (including handleCommand, handleEvents, handleSnapshot). Then it becomes clear to a user what they should implement and the control flow is more obvious and explicit. It will also be a compile error rather than runtime if something is missing. I also think we should really avoid finding methods by convention, matching against their types with reflection - I haven't seen that kind of approach in a mainstream golang library, and I think it may dissuade users from using it.

cloudstate/eventsourced.go Show resolved Hide resolved
cloudstate/eventsourced.go Show resolved Hide resolved
for {
if failed != nil {
return failed
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come we close the stream if the user function returned an error? Shouldn't we just return the protocol.Failure and keep processing the stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this here is ok; handleFailure will handle every error out of any Command, Event or Init msg and itself sends a protocol.Failure and if sending this failure does not work, we close the stream.

But: I see now handleFailure lets the failure through if it does not match. Will fix this.

Edit: while thinking more about this I'm not sure if an error (failure) that is not a cloudState.ErrFailure or cloudstate.ErrClientActionFailure as in handleFailure checked indeed should close the stream as it is an error we would not expect.

cloudstate/eventsourced.go Show resolved Hide resolved
cloudstate/eventsourced.go Outdated Show resolved Hide resolved
tck/cmd/tck_shoppingcart/shoppingcart.go Show resolved Hide resolved

type SnapshotHandler interface {
HandleSnapshot(snapshot interface{}) (handled bool, err error)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how we can make this a bit more explicit. In other words there is no compilation error if a user doesn't implement these functions - only at runtime. Maybe we should require the event sourced entity to implement them all?

cloudstate/eventsourced.go Show resolved Hide resolved
@marcellanz
Copy link
Contributor Author

Great feedback and input on this PR. I'm still on some days off but will have a look soon on it.

@viktorklang
Copy link
Contributor

@marcellanz Is this PR ready to merge? /cc @jroper @jsravn

@marcellanz
Copy link
Contributor Author

@marcellanz Is this PR ready to merge? /cc @jroper @jsravn

@viktorklang I'd say we can merge it tomorrow, perhaps after the contributors call. James had some great input and I made some changes accordingly. I'll push that tomorrow evening.

@viktorklang
Copy link
Contributor

@marcellanz Awesome, looking forward to the call!

@viktorklang
Copy link
Contributor

Ship it!

@viktorklang viktorklang merged commit b89ecf5 into cloudstateio:master Oct 22, 2019
viktorklang pushed a commit to cloudstateio/cloudstate that referenced this pull request Oct 29, 2019
* [go-support] as discussed in weekly call 2019-10-08: go_packages added to the main repo.

* [go-support] paradox documentation for go-support.

* [go-support] enable the TCK to run the shopping cart example for Go.

* [PR Review Feedback] first batch of changes after review of cloudstateio/go-support#5

* [go-support] Update doc and config to Cloudstate instead of CloudState

* [PR-review] fixes and issues addressed from the review feedback as well as API changes reflected from the latest updates to the go-support repo.

* [PR-review] cross-PR review changes from go-support PR #10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Golang support
5 participants