-
Notifications
You must be signed in to change notification settings - Fork 3
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
ENG-6204: Implement token based streaming #140
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
84d64e8
implement token based streaming
erickpintor 3678acd
unify error handling; drop internal coroutine
erickpintor c878d9d
drop unused argument attemps in parseQueryResponse
erickpintor 38d02c6
reuse the same sandbox in the examples, if present
erickpintor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package fauna | ||
|
||
import ( | ||
"encoding/json" | ||
"io" | ||
) | ||
|
||
// EventType represents a Fauna's event type. | ||
type EventType string | ||
|
||
const ( | ||
// AddEvent happens when a new value is added to the stream's watched set. | ||
AddEvent EventType = "add" | ||
// UpdateEvent happens when a value in the stream's watched set changes. | ||
UpdateEvent EventType = "update" | ||
// Remove event happens when a value in the stream's watched set is removed. | ||
RemoveEvent EventType = "remove" | ||
// StatusEvent happens periodically and comunicates the stream's latest | ||
// transacion time as well as ops aquired during its idle period. | ||
StatusEvent EventType = "status" | ||
) | ||
|
||
// Event represents a streaming event. | ||
// | ||
// Events of type [fauna.StatusEvent] have its [fauna.Event.Data] field set to | ||
// nil. Other event's [fauna.Data] can be unmarshalled via the | ||
// [fauna.Event.Unmarshal] method. | ||
type Event struct { | ||
// Type is this event's type. | ||
Type EventType | ||
// TxnTime is the transaction time that produce this event. | ||
TxnTime int64 | ||
// Data is the event's data. | ||
Data any | ||
// Stats contains the ops acquired to process the event. | ||
Stats Stats | ||
} | ||
|
||
// Unmarshal will unmarshal the raw [fauna.Event.Data] (if present) into the | ||
// known type provided as `into`. `into` must be a pointer to a map or struct. | ||
func (e *Event) Unmarshal(into any) error { | ||
return decodeInto(e.Data, into) | ||
} | ||
|
||
// ErrEvent contains error information present in error events. | ||
// | ||
// Error events with "abort" code contain its aborting value present in the | ||
// [fauan.ErrEvent.Abort]. The aborting values can be unmarshalled with the | ||
// [fauna.ErrEvent.Unmarshal] method. | ||
type ErrEvent struct { | ||
// Code is the error's code. | ||
Code string `json:"code"` | ||
|
||
// Message is the error's message. | ||
Message string `json:"message"` | ||
|
||
// Abort is the error's abort data, present if [fauna.ErrEvent.Code] is | ||
// equals to "abort". | ||
Abort any `json:"abort,omitempty"` | ||
} | ||
|
||
// Error provides the underlying error message. | ||
func (e *ErrEvent) Error() string { | ||
return e.Message | ||
} | ||
|
||
// Unmarshal will unmarshal the raw [fauna.ErrEvent.Abort] (if present) into the | ||
// known type provided as `into`. `into` must be a pointer to a map or struct. | ||
func (e *ErrEvent) Unmarshal(into any) error { | ||
return decodeInto(e.Abort, into) | ||
} | ||
|
||
// Events is an iterator of Fauna events. | ||
// | ||
// The next available event can be obtained by calling the | ||
// [fauna.Subscription.Next] method. Note this method blocks until the next | ||
// event is available or until the events iterator is closed via the | ||
// [fauna.Events.Close] method. | ||
type Events struct { | ||
byteStream io.ReadCloser | ||
decoder *json.Decoder | ||
} | ||
|
||
func newEvents(byteStream io.ReadCloser) *Events { | ||
return &Events{ | ||
byteStream: byteStream, | ||
decoder: json.NewDecoder(byteStream), | ||
} | ||
} | ||
|
||
// Close gracefully closes the stream subscription. | ||
func (es *Events) Close() (err error) { | ||
// XXX: Is there a way to make sure there are no bytes left on the stream | ||
// after closing it? According to go's docs, the underlying connection will | ||
// remain unusable for the duration of its idle time if there are bytes left | ||
// in its read buffer. | ||
return es.byteStream.Close() | ||
} | ||
|
||
type rawEvent = struct { | ||
Type EventType `json:"type"` | ||
TxnTime int64 `json:"txn_ts"` | ||
Data any `json:"data,omitempty"` | ||
Error *ErrEvent `json:"error,omitempty"` | ||
Stats Stats `json:"stats"` | ||
} | ||
|
||
// Next blocks until the next event is available. | ||
// | ||
// Note that network errors of type [fauna.ErrEvent] are considered fatal and | ||
// close the underlying stream. Calling next after an error event occurs will | ||
// return an error. | ||
func (es *Events) Next() (event *Event, err error) { | ||
raw := rawEvent{} | ||
if err = es.decoder.Decode(&raw); err == nil { | ||
event, err = convertRawEvent(&raw) | ||
if _, ok := err.(*ErrEvent); ok { | ||
es.Close() // no more events are comming | ||
} | ||
} | ||
return | ||
} | ||
|
||
func convertRawEvent(raw *rawEvent) (event *Event, err error) { | ||
if raw.Error != nil { | ||
if raw.Error.Abort != nil { | ||
if raw.Error.Abort, err = convert(false, raw.Error.Abort); err != nil { | ||
return | ||
} | ||
} | ||
err = raw.Error | ||
} else { | ||
if raw.Data != nil { | ||
if raw.Data, err = convert(false, raw.Data); err != nil { | ||
return | ||
} | ||
} | ||
event = &Event{ | ||
Type: raw.Type, | ||
TxnTime: raw.TxnTime, | ||
Data: raw.Data, | ||
Stats: raw.Stats, | ||
} | ||
} | ||
return | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, probably want to drain on close. There might be a more elegant way to do this, but we've implemented this in the past via io.Copy
e.g. something like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I'll look into it soon. We need to be careful because if the stream is still open, calling read on the bytes stream will block.