Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide more flexibility on how EventStream specs are stored #120

Merged
merged 13 commits into from
Jan 22, 2024

Conversation

peterbroadhurst
Copy link
Contributor

@peterbroadhurst peterbroadhurst commented Jan 21, 2024

I've hit some problems in having enough flexibility on the Event Streams package, and this PR proposes to solve these:

  1. The config sub-section on the input/output object is odd
    • You configure websockets/webhooks with a sub-structure named the same name at the top level, why if you're doing something else do you have to nest it underneath a random config JSON
  2. Removing access to the type field - I have some cases where ES are exposed in multiple API endpoints, with different restrictions on the type. Specifically one case where the configuration for using event streams internally, vs. externally.
  3. Removing advertising webhooks/websockets fields, if those types are not actually used
  4. Customizing the persistence implementation, rather coupled to 1-3 above.

Overall this approach makes the package more flexible, and while it requires a small change to consumers (per the example one), I believe all previous cases can be solved without DB migration.

Also fixes a significant issue, where the foreground context of a HTTP call is used to init a stream on UpsertStream.

@codecov-commenter
Copy link

codecov-commenter commented Jan 21, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (ba21054) 99.93% compared to head (00303f7) 99.98%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #120      +/-   ##
==========================================
+ Coverage   99.93%   99.98%   +0.04%     
==========================================
  Files          78       78              
  Lines        6416     6437      +21     
==========================================
+ Hits         6412     6436      +24     
+ Misses          3        1       -2     
+ Partials        1        0       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -52,8 +53,64 @@ var CheckpointFilters = &ffapi.QueryFields{

type IDValidator func(ctx context.Context, idStr string) error

func NewEventStreamPersistence[CT any](db *dbsql.Database, idValidator IDValidator) Persistence[CT] {
return &esPersistence[CT]{
type GenericEventStream struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is probably the most meaningful starting point for understanding the change.

Before: The had an EventStreamSpec[CT any] that was a struct and you must use it, and put any special config you have under a config subsection.

After: This GenericEventStream is a struct that you can use if you don't have any special config, or you can copy+extend it to add in the config you need.

This was the best compromise I could find that gave the flexibility, without making massive changes.

@@ -68,8 +125,8 @@ func (p *esPersistence[CT]) IDValidator() IDValidator {
return p.idValidator
}

func (p *esPersistence[CT]) EventStreams() dbsql.CRUD[*EventStreamSpec[CT]] {
return &dbsql.CrudBase[*EventStreamSpec[CT]]{
func (p *esPersistence[CT]) EventStreams() dbsql.CRUD[*GenericEventStream] {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This too has gone from being a mandated implementation, to a reference for you to extend.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... which matches up with the fact that the DB migrations are described as reference already, so overall I feel it's much more consistent this way

@@ -9,7 +9,6 @@ CREATE TABLE eventstreams (
initial_sequence_id TEXT,
topic_filter TEXT,
identity TEXT,
config TEXT,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are no longer constrained to this approach. If you want first-class indexed columns for your extra config, you can do it. However, you have to take ownership of the whole CRUD implementation in that case (rather than using NewGenericEventStreamPersistence).

Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
InitialSequenceID *string `ffstruct:"eventstream" json:"initialSequenceID,omitempty"`
TopicFilter *string `ffstruct:"eventstream" json:"topicFilter,omitempty"`
Identity *string `ffstruct:"eventstream" json:"identity,omitempty"`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was an example of a field that needed to be top-level for one implementation for DB indexing, and with the previous structure that meant pushing it down to this common layer.

Now we've addressed that form of extensibility properly, it can go.

Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
Signed-off-by: Peter Broadhurst <[email protected]>
"status": &ffapi.StringField{},
"type": &ffapi.StringField{},
"topicfilter": &ffapi.StringField{},
"identity": &ffapi.StringField{},
Copy link
Contributor

Choose a reason for hiding this comment

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

is this still filterable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good spot, thanks. This should be removed.
An implementation that has extra fields, has to create their own filter list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also adding some comments to the code to clear up the role of GenericEventStream and these base filters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok - commit added now with that

@@ -92,16 +149,17 @@ func (p *esPersistence[CT]) EventStreams() dbsql.CRUD[*EventStreamSpec[CT]] {
"websocket_config",
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm trying to wrap my head around the migration impact to anything using this before the change. What was stored in "config" vs "webhook_config" and "websocket_config"? Will migration involve extending this generic implementation to continue storing data that was in the removed columns "identity" and "config"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes - exactly.
You can still choose to store that configuration in DB fields called identity and config, but you're no longer forced to.

If you were doing so before, you would indeed need to move to using your own Persistence implementation - in fact this library is very opinionated now that if you do want to extend you should take on the persistence implementation yourself.

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 understand it's quite a big change to the interface, where the migration impact is on consumers who have extended it.

The arguments really for the change are:

  1. It's a rather new feature in the library, and I don't have evidence it's used widely yet, so it's a good time to address fundamental issues.
  2. As someone trying to use the extensibility before, I found it was extremely difficult to extend previously. Now it's much easier. For me the code migration on the consuming side (about a 1/2 day) was well, well worth it in terms of where it left me for future extensibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, makes sense - and the comments added in the code are very helpful to that effect. I agree we're trying to feel out the best way to provide an extensible implementation, and it's probably best to make these changes now.

@peterbroadhurst peterbroadhurst merged commit a4a1c3c into main Jan 22, 2024
2 checks passed
@peterbroadhurst peterbroadhurst deleted the es-flexibility branch January 22, 2024 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants