Repository navigation
Expand file tree
/
Copy path_dtypes.py
More file actions
360 lines (282 loc) · 9.35 KB
/
Copy path_dtypes.py
File metadata and controls
360 lines (282 loc) · 9.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
from abc import ABCMeta, abstractmethod
from asyncio import Task
from dataclasses import dataclass
from datetime import datetime
from enum import IntEnum
from typing import TypeVar, Callable, Union, Optional
from collections.abc import AsyncIterable
from pynumaflow.shared.asynciter import NonBlockingIterator
from pynumaflow._constants import DROP
M = TypeVar("M", bound="Message")
class WindowOperation(IntEnum):
"""
Enumerate the type of Window operation received.
"""
OPEN = (0,)
CLOSE = (1,)
APPEND = (4,)
@dataclass(init=False)
class Datum:
"""
Class to define the important information for the event.
Args:
keys: the keys of the event.
value: the payload of the event.
event_time: the event time of the event.
watermark: the watermark of the event.
>>> # Example usage
>>> from pynumaflow.reducer import Datum
>>> from datetime import datetime, timezone
>>> payload = bytes("test_mock_message", encoding="utf-8")
>>> t1 = datetime.fromtimestamp(1662998400, timezone.utc)
>>> t2 = datetime.fromtimestamp(1662998460, timezone.utc)
>>> msg_headers = {"key1": "value1", "key2": "value2"}
>>> d = Datum(
... keys=["test_key"],
... value=payload,
... event_time=t1,
... watermark=t2,
... headers=msg_headers
... )
"""
__slots__ = ("_keys", "_value", "_event_time", "_watermark", "_headers")
_keys: list[str]
_value: bytes
_event_time: datetime
_watermark: datetime
_headers: dict[str, str]
def __init__(
self,
keys: list[str],
value: bytes,
event_time: datetime,
watermark: datetime,
headers: Optional[dict[str, str]] = None,
):
self._keys = keys or list()
self._value = value or b""
if not isinstance(event_time, datetime):
raise TypeError(f"Wrong data type: {type(event_time)} for Datum.event_time")
self._event_time = event_time
if not isinstance(watermark, datetime):
raise TypeError(f"Wrong data type: {type(watermark)} for Datum.watermark")
self._watermark = watermark
self._headers = headers or {}
@property
def keys(self) -> list[str]:
"""Returns the keys of the event"""
return self._keys
@property
def value(self) -> bytes:
"""Returns the value of the event."""
return self._value
@property
def event_time(self) -> datetime:
"""Returns the event time of the event."""
return self._event_time
@property
def watermark(self) -> datetime:
"""Returns the watermark of the event."""
return self._watermark
@property
def headers(self) -> dict[str, str]:
"""Returns the headers of the event."""
return self._headers
@dataclass(init=False)
class IntervalWindow:
"""Defines the start and end of the interval window for the event."""
__slots__ = ("_start", "_end")
_start: datetime
_end: datetime
def __init__(self, start: datetime, end: datetime):
self._start = start
self._end = end
@property
def start(self):
"""Returns the start point of the interval window."""
return self._start
@property
def end(self):
"""Returns the end point of the interval window."""
return self._end
@dataclass(init=False)
class ReduceWindow:
"""
Defines the window for a reduce operation which includes the
interval window along with the slot.
"""
__slots__ = ("_window", "_slot")
_window: IntervalWindow
_slot: str
def __init__(self, start: datetime, end: datetime, slot: str = ""):
self._window = IntervalWindow(start=start, end=end)
self._slot = slot
@property
def start(self):
"""Returns the start point of the interval window."""
return self._window.start
@property
def end(self):
"""Returns the end point of the interval window."""
return self._window.end
@property
def slot(self):
"""Returns the slot from the window"""
return self._slot
@property
def window(self):
"""Return the interval window"""
return self._window
@dataclass(init=False)
class Metadata:
"""Defines the metadata for the event."""
__slots__ = ("_interval_window",)
_interval_window: IntervalWindow
def __init__(self, interval_window: IntervalWindow):
self._interval_window = interval_window
@property
def interval_window(self):
"""Returns the interval window for the event."""
return self._interval_window
@dataclass
class ReduceResult:
"""Defines the object to hold the result of reduce streaming computation."""
__slots__ = ("_future", "_iterator", "_key", "_window", "_result_queue", "_consumer_future")
_future: Task
_iterator: NonBlockingIterator
_key: list[str]
_window: ReduceWindow
_result_queue: NonBlockingIterator
_consumer_future: Task
@property
def future(self):
"""Returns the future result of computation."""
return self._future
@property
def iterator(self):
"""Returns the handle to the producer queue."""
return self._iterator
@property
def keys(self) -> list[str]:
"""Returns the keys of the partition."""
return self._key
@property
def window(self) -> ReduceWindow:
"""Returns the window for the current reduce request"""
return self._window
@property
def result_queue(self):
"""Returns the async queue used to write the output for the tasks"""
return self._result_queue
@property
def consumer_future(self):
"""Returns the async consumer task for the result queue"""
return self._consumer_future
@dataclass
class ReduceRequest:
"""Defines the object to hold a request for the reduce operation."""
__slots__ = ("_operation", "_windows", "_payload")
_operation: WindowOperation
_windows: list[ReduceWindow]
_payload: Datum
def __init__(self, operation: WindowOperation, windows: list[ReduceWindow], payload: Datum):
self._operation = operation
self._windows = windows
self._payload = payload
@property
def operation(self) -> WindowOperation:
"""Returns the future result of computation."""
return self._operation
@property
def windows(self) -> list[ReduceWindow]:
"""Returns the handle to the producer queue."""
return self._windows
@property
def payload(self) -> Datum:
"""Returns the payload of the window."""
return self._payload
@dataclass(init=False)
class Message:
"""
Basic datatype for data passing to the next vertex/vertices.
Args:
value: data in bytes
keys: []string keys for vertex (optional)
tags: []string tags for conditional forwarding (optional)
"""
__slots__ = ("_value", "_keys", "_tags")
_value: bytes
_keys: list[str]
_tags: list[str]
def __init__(
self,
value: bytes,
keys: list[str] = None,
tags: list[str] = None,
):
"""
Creates a Message object to send value to a vertex.
"""
self._keys = keys or []
self._tags = tags or []
self._value = value or b""
# self._window = window or None
# returns the Message Object which will be dropped
@classmethod
def to_drop(cls: type[M]) -> M:
return cls(b"", None, [DROP])
@property
def value(self) -> bytes:
return self._value
@property
def keys(self) -> list[str]:
return self._keys
@property
def tags(self) -> list[str]:
return self._tags
ReduceStreamAsyncCallable = Callable[
[list[str], AsyncIterable[Datum], NonBlockingIterator, Metadata], None
]
class ReduceStreamer(metaclass=ABCMeta):
"""
Provides an interface to write a ReduceStreamer
which will be exposed over a gRPC server.
"""
def __call__(self, *args, **kwargs):
"""
Allow to call handler function directly if class instance is sent
as the reducer_instance.
"""
return self.handler(*args, **kwargs)
@abstractmethod
async def handler(
self,
keys: list[str],
datums: AsyncIterable[Datum],
output: NonBlockingIterator,
md: Metadata,
):
"""
Implement this handler function which implements the ReduceStreamCallable interface.
"""
pass
class _ReduceStreamBuilderClass:
"""
Class to build a ReduceStreamer class instance.
Used Internally
Args:
reducer_class: the reducer class to be used for Reduce UDF
args: the arguments to be passed to the reducer class
kwargs: the keyword arguments to be passed to the reducer class
"""
def __init__(self, reducer_class: type[ReduceStreamer], args: tuple, kwargs: dict):
self._reducer_class: type[ReduceStreamer] = reducer_class
self._args = args
self._kwargs = kwargs
def create(self) -> ReduceStreamer:
"""
Create a new ReduceStreamer instance.
"""
return self._reducer_class(*self._args, **self._kwargs)
# ReduceStreamCallable is a callable which can be used as a handler for the Reduce UDF.
ReduceStreamCallable = Union[ReduceStreamAsyncCallable, type[ReduceStreamer]]