You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/design/event-streams.md
+87-19Lines changed: 87 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -89,15 +89,19 @@ structure ThrottlingError {}
89
89
90
90
### Event Stream Type Representation
91
91
92
-
The members of an operation input or output that target a stream will be represented with an asynchronous [Flow](https://kotlinlang.org/docs/reference/coroutines/flow.html)
93
-
from the `kotlinx-coroutines-core` library. `Flow` is a natural fit for representing asynchronous streams.
92
+
The members of an operation input or output that target a stream will be represented with an asynchronous
93
+
[Flow](https://kotlinlang.org/docs/reference/coroutines/flow.html) from the `kotlinx-coroutines-core` library.
94
+
`Flow` is a natural fit for representing asynchronous streams.
94
95
95
-
`Flow` was chosen for pagination and already in use as part of our public API contract. Any alternative to this would require a custom but similar type that doesn't play well with
96
-
the rest of the coroutine ecosystem. There is also prior art for representing streaming requests and responses, see [gRPC Kotlin](https://github.com/grpc/grpc-kotlin).
96
+
`Flow` was chosen for pagination and already in use as part of our public API contract. Any alternative to this
97
+
would require a custom but similar type that doesn't play well with the rest of the coroutine ecosystem. There is
98
+
also prior art for representing streaming requests and responses, see [gRPC Kotlin](https://github.com/grpc/grpc-kotlin).
97
99
98
100
The following types and service would be generated.
99
101
100
-
NOTE: only the input and output types are shown, the other structures or unions in the model would be generated as described in [Kotlin Smithy Design](kotlin-smithy-sdk.md).
102
+
NOTE: only the input and output types are shown, the other structures or unions in the model would be generated as
103
+
described in [Kotlin Smithy Design](kotlin-smithy-sdk.md) with the exception of event stream members targeting errors
104
+
which is described below in more detail.
101
105
102
106
#### Input Event Streams
103
107
@@ -120,7 +124,8 @@ class PublishMessagesRequest private constructor(builder: Builder){
120
124
121
125
#### Output Event Streams
122
126
123
-
Output event streams would be modeled the same way as input streams. The response object would have a `Flow<T>` field that represents the response stream.
127
+
Output event streams would be modeled the same way as input streams. The response object would have a `Flow<T>`
@@ -137,20 +142,81 @@ class SubscribeToMovementsResponse private constructor(builder: Builder){
137
142
```
138
143
139
144
140
-
Modeling the event stream as a field of the request or response allows for [initial messages](https://awslabs.github.io/smithy/1.0/spec/core/stream-traits.html#initial-messages)
141
-
to be implemented. If we directly returned or took a `Flow<T>` as the input or output type we would not be able to represent the initial request or response fields when present.
145
+
Modeling the event stream as a field of the request or response allows for
146
+
[initial messages](https://awslabs.github.io/smithy/1.0/spec/core/stream-traits.html#initial-messages) to be
147
+
implemented. If we directly returned or took a `Flow<T>` as the input or output type we would not be able to
148
+
represent the initial request or response fields when present.
142
149
143
150
151
+
#### Event Stream Error Representation
152
+
153
+
Event stream unions may model exceptions that can appear on the stream. These exceptions are terminal messages that
154
+
are intended to be surfaced to the client using idiomatic error handling mechanisms of the target language. Thus,
155
+
the modeled errors a consumer may see on the stream are part of the overall union that makes up the possible events.
156
+
157
+
NOTE: the set of errors on the operation MAY not be the same set of errors modeled on the event stream.
158
+
159
+
160
+
Using the example from above:
161
+
162
+
```
163
+
@streaming
164
+
union MovementEvents {
165
+
up: Movement,
166
+
down: Movement,
167
+
left: Movement,
168
+
right: Movement,
169
+
throttlingError: ThrottlingError
170
+
}
171
+
172
+
```
173
+
174
+
The default representation of a union (as documented in [Kotlin Smithy Design](kotlin-smithy-sdk.md)) is generated as:
175
+
176
+
```kotlin
177
+
sealedclassMovementEvents {
178
+
data classUp(valvalue:Movement): MovementEvents()
179
+
data classDown(valvalue:Movement): MovementEvents()
180
+
data classLeft(valvalue:Movement): MovementEvents()
181
+
data classRight(valvalue:Movement): MovementEvents()
182
+
data classThrottlingError(valvalue:ThrottlingError): MovementEvents()
183
+
object SdkUnknown : MovementEvents()
184
+
}
185
+
```
186
+
187
+
This is undesirable though since event stream errors are terminal and end the stream. Keeping them in the set of
188
+
possible events also means it may be easier for consumers to ignore errors depending on what events they are looking
189
+
for (e.g. by having a catch all `else` branch they may inadvertently ignore an error and think the stream completed
190
+
successfully).
191
+
192
+
193
+
Event stream unions will be special-cased to filter out variants targeting error shapes. When these errors are
194
+
emitted by the service on the stream they will be converted to the appropriate modeled exception and thrown rather
195
+
than being emitted on the stream the consumer sees.
196
+
197
+
As an example, the generated event stream union will look like this (note the absence of `ThrottlingError`):
198
+
199
+
```kotlin
200
+
sealedclassMovementEvents {
201
+
data classUp(valvalue:Movement): MovementEvents()
202
+
data classDown(valvalue:Movement): MovementEvents()
203
+
data classLeft(valvalue:Movement): MovementEvents()
204
+
data classRight(valvalue:Movement): MovementEvents()
205
+
object SdkUnknown : MovementEvents()
206
+
}
207
+
```
208
+
144
209
### **Service and Usage**
145
210
146
211
NOTE: There are types and internal details here not important to the design of how customers will interact with
Those details are subject to change and not part of this design document. The focus here is on the way
149
-
streaming is exposed to a customer.
212
+
streaming requests/responses (e.g. serialization/deserialization). Those details are subject to change and not part of
213
+
this design document. The focus here is on the way streaming is exposed to a customer.
150
214
151
215
152
-
The signatures generated match that of binary streaming requests and responses. Notably that output streams take a lambda instead of returning the response directly (see [binary-streaming design](binary-streaming.md) which discusses this pattern).
153
-
The response (and event stream) are only valid in that scope, after which the resources consumed by the stream are closed and no longer valid.
216
+
The signatures generated match that of binary streaming requests and responses. Notably that output streams take a
217
+
lambda instead of returning the response directly (see [binary-streaming design](binary-streaming.md) which
218
+
discusses this pattern). The response (and event stream) are only valid in that scope, after which the resources
219
+
consumed by the stream are closed and no longer valid.
Accepting a lambda matches what is generated for binary streams (see [binary-streaming design](binary-streaming.md)) and will provide a consistent API experience as well
222
-
as the same benefits to the SDK (properly scoped lifetime for resources).
286
+
Accepting a lambda matches what is generated for binary streams (see [binary-streaming design](binary-streaming.md))
287
+
and will provide a consistent API experience as well as the same benefits to the SDK (properly scoped lifetime
288
+
for resources).
223
289
224
290
225
291
# Appendix
@@ -228,9 +294,10 @@ as the same benefits to the SDK (properly scoped lifetime for resources).
228
294
## Java Interop
229
295
230
296
`Flow<T>` is not easily consumable directly from Java due to the `suspend` nature of it. JetBrains provides
231
-
[reactive adapters](https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive) that can be used to convert rxJava and JDK-9
232
-
reactive streams to or from an equivalent `Flow`. Users would be responsible for creating a shim layer using these primitives provided
233
-
by JetBrains which would allow them to expose the Kotlin functions however they see fit to their applications.
297
+
[reactive adapters](https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive) that can be used to convert
298
+
rxJava and JDK-9 reactive streams to or from an equivalent `Flow`. Users would be responsible for creating a shim
299
+
layer using these primitives provided by JetBrains which would allow them to expose the Kotlin functions however
300
+
they see fit to their applications.
234
301
235
302
236
303
## Additional References
@@ -243,4 +310,5 @@ by JetBrains which would allow them to expose the Kotlin functions however they
243
310
244
311
# Revision history
245
312
313
+
* 02/17/2022 - Remove errors from generated event stream
0 commit comments