Skip to content

Commit e717b1b

Browse files
authored
feat: udsink gRPC stream (#34)
1 parent ad55710 commit e717b1b

6 files changed

Lines changed: 39 additions & 57 deletions

File tree

examples/sink/simplesink/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
1. Build the docker image, and push
44
```shell
55
make image
6-
# Privilege requried
6+
# Privilege required
77
docker push quay.io/numaio/simplesink-example:python
88
```
99

pynumaflow/sink/_udfunction_pb2.pyi

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ class Datum(_message.Message):
5151
watermark: _Optional[_timestamp_pb2.Timestamp] = ...,
5252
) -> None: ...
5353

54-
class DatumList(_message.Message):
55-
__slots__ = ["elements"]
56-
ELEMENTS_FIELD_NUMBER: _ClassVar[int]
57-
elements: List[Datum]
58-
def __init__(self, elements: _Optional[List[Datum]]) -> None: ...
59-
6054
class Response(_message.Message):
6155
__slots__ = ["id", "success", "err_msg"]
6256
ID_FIELD_NUMBER: _ClassVar[int]

pynumaflow/sink/generated/udsink_pb2.py

Lines changed: 10 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pynumaflow/sink/generated/udsink_pb2_grpc.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ def __init__(self, channel):
1515
Args:
1616
channel: A grpc.Channel.
1717
"""
18-
self.SinkFn = channel.unary_unary(
18+
self.SinkFn = channel.stream_unary(
1919
"/sink.v1.UserDefinedSink/SinkFn",
20-
request_serializer=udsink__pb2.DatumList.SerializeToString,
20+
request_serializer=udsink__pb2.Datum.SerializeToString,
2121
response_deserializer=udsink__pb2.ResponseList.FromString,
2222
)
2323
self.IsReady = channel.unary_unary(
@@ -30,7 +30,7 @@ def __init__(self, channel):
3030
class UserDefinedSinkServicer(object):
3131
"""Missing associated documentation comment in .proto file."""
3232

33-
def SinkFn(self, request, context):
33+
def SinkFn(self, request_iterator, context):
3434
"""SinkFn writes the Datum to a user defined sink."""
3535
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
3636
context.set_details("Method not implemented!")
@@ -45,9 +45,9 @@ def IsReady(self, request, context):
4545

4646
def add_UserDefinedSinkServicer_to_server(servicer, server):
4747
rpc_method_handlers = {
48-
"SinkFn": grpc.unary_unary_rpc_method_handler(
48+
"SinkFn": grpc.stream_unary_rpc_method_handler(
4949
servicer.SinkFn,
50-
request_deserializer=udsink__pb2.DatumList.FromString,
50+
request_deserializer=udsink__pb2.Datum.FromString,
5151
response_serializer=udsink__pb2.ResponseList.SerializeToString,
5252
),
5353
"IsReady": grpc.unary_unary_rpc_method_handler(
@@ -68,7 +68,7 @@ class UserDefinedSink(object):
6868

6969
@staticmethod
7070
def SinkFn(
71-
request,
71+
request_iterator,
7272
target,
7373
options=(),
7474
channel_credentials=None,
@@ -79,11 +79,11 @@ def SinkFn(
7979
timeout=None,
8080
metadata=None,
8181
):
82-
return grpc.experimental.unary_unary(
83-
request,
82+
return grpc.experimental.stream_unary(
83+
request_iterator,
8484
target,
8585
"/sink.v1.UserDefinedSink/SinkFn",
86-
udsink__pb2.DatumList.SerializeToString,
86+
udsink__pb2.Datum.SerializeToString,
8787
udsink__pb2.ResponseList.FromString,
8888
options,
8989
channel_credentials,

pynumaflow/sink/server.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import multiprocessing
44
import os
55
from concurrent.futures import ThreadPoolExecutor
6-
from typing import Callable, List
6+
from typing import Callable, Iterator
77

88
import grpc
99
from google.protobuf import empty_pb2 as _empty_pb2
@@ -21,7 +21,7 @@
2121

2222
_LOGGER = logging.getLogger(__name__)
2323

24-
UDSinkCallable = Callable[[List[Datum]], Responses]
24+
UDSinkCallable = Callable[[Iterator[Datum]], Responses]
2525
_PROCESS_COUNT = multiprocessing.cpu_count()
2626
MAX_THREADS = int(os.getenv("MAX_THREADS", 0)) or (_PROCESS_COUNT * 4)
2727

@@ -41,7 +41,7 @@ class UserDefinedSinkServicer(udsink_pb2_grpc.UserDefinedSinkServicer):
4141
Example invocation:
4242
>>> from typing import List
4343
>>> from pynumaflow.sink import Datum, Responses, Response, UserDefinedSinkServicer
44-
>>> def udsink_handler(datums: List[Datum]) -> Responses:
44+
>>> def udsink_handler(datums: Iterator[Datum]) -> Responses:
4545
... responses = Responses()
4646
... for msg in datums:
4747
... responses.append(Response.as_success(msg.id))
@@ -69,20 +69,20 @@ def __init__(
6969
]
7070

7171
def SinkFn(
72-
self, request: udsink_pb2.DatumList, context: NumaflowServicerContext
72+
self, request_iterator: Iterator[Datum], context: NumaflowServicerContext
7373
) -> udsink_pb2.ResponseList:
7474
"""
7575
Applies a sink function to a list of datum elements.
7676
The pascal case function name comes from the generated udsink_pb2_grpc.py file.
7777
"""
7878
# if there is an exception, we will mark all the responses as a failure
7979
try:
80-
rspns = self.__sink_handler(request.elements)
80+
rspns = self.__sink_handler(request_iterator)
8181
except Exception as err:
8282
err_msg = "UDSinkError: %r" % err
8383
_LOGGER.critical(err_msg, exc_info=True)
8484
rspns = Responses()
85-
for _datum in request.elements:
85+
for _datum in request_iterator:
8686
rspns.append(Response.as_failure(_datum.id, err_msg))
8787

8888
responses = []

pynumaflow/tests/sink/test_server.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22
from datetime import datetime, timezone
3-
from typing import List
3+
from typing import Iterator
44

55
from google.protobuf import empty_pb2 as _empty_pb2
66
from google.protobuf import timestamp_pb2 as _timestamp_pb2
@@ -11,7 +11,7 @@
1111
from pynumaflow.sink.generated import udsink_pb2
1212

1313

14-
def udsink_handler(datums: List[Datum]) -> Responses:
14+
def udsink_handler(datums: Iterator[Datum]) -> Responses:
1515
responses = Responses()
1616
for msg in datums:
1717
if "err" in msg.value.decode("utf-8"):
@@ -21,7 +21,7 @@ def udsink_handler(datums: List[Datum]) -> Responses:
2121
return responses
2222

2323

24-
def err_udsink_handler(_: List[Datum]) -> Responses:
24+
def err_udsink_handler(_: Iterator[Datum]) -> Responses:
2525
raise RuntimeError("Something is fishy!")
2626

2727

@@ -91,17 +91,18 @@ def test_udsink_err(self):
9191
),
9292
]
9393

94-
request = udsink_pb2.DatumList(elements=test_datums)
95-
96-
method = self.test_server.invoke_unary_unary(
94+
method = self.test_server.invoke_stream_unary(
9795
method_descriptor=(
9896
udsink_pb2.DESCRIPTOR.services_by_name["UserDefinedSink"].methods_by_name["SinkFn"]
9997
),
10098
invocation_metadata={},
101-
request=request,
10299
timeout=1,
103100
)
104101

102+
method.send_request(test_datums[0])
103+
method.send_request(test_datums[1])
104+
method.requests_closed()
105+
105106
response, metadata, code, details = method.termination()
106107
self.assertEqual(2, len(response.responses))
107108
self.assertEqual("test_id_0", response.responses[0].id)
@@ -133,17 +134,18 @@ def test_forward_message(self):
133134
),
134135
]
135136

136-
request = udsink_pb2.DatumList(elements=test_datums)
137-
138-
method = self.test_server.invoke_unary_unary(
137+
method = self.test_server.invoke_stream_unary(
139138
method_descriptor=(
140139
udsink_pb2.DESCRIPTOR.services_by_name["UserDefinedSink"].methods_by_name["SinkFn"]
141140
),
142141
invocation_metadata={},
143-
request=request,
144142
timeout=1,
145143
)
146144

145+
method.send_request(test_datums[0])
146+
method.send_request(test_datums[1])
147+
method.requests_closed()
148+
147149
response, metadata, code, details = method.termination()
148150
self.assertEqual(2, len(response.responses))
149151
self.assertEqual("test_id_0", response.responses[0].id)

0 commit comments

Comments
 (0)