airbyte-cdk 0.58.8__py3-none-any.whl → 0.59.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +20 -21
- airbyte_cdk/sources/concurrent_source/concurrent_source.py +4 -3
- airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +15 -18
- airbyte_cdk/sources/concurrent_source/throttler.py +25 -0
- airbyte_cdk/sources/streams/concurrent/cursor.py +29 -8
- airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +3 -5
- airbyte_cdk/sources/streams/concurrent/partition_reader.py +3 -4
- airbyte_cdk/sources/streams/concurrent/partitions/throttled_queue.py +41 -0
- airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +6 -12
- airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +36 -30
- {airbyte_cdk-0.58.8.dist-info → airbyte_cdk-0.59.0.dist-info}/METADATA +1 -1
- {airbyte_cdk-0.58.8.dist-info → airbyte_cdk-0.59.0.dist-info}/RECORD +23 -19
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py +2 -2
- unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py +4 -10
- unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py +82 -12
- unit_tests/sources/streams/concurrent/test_cursor.py +20 -3
- unit_tests/sources/streams/concurrent/test_datetime_state_converter.py +166 -268
- unit_tests/sources/streams/concurrent/test_thread_pool_manager.py +2 -15
- unit_tests/sources/streams/concurrent/test_throttled_queue.py +65 -0
- unit_tests/sources/streams/concurrent/test_throttler.py +13 -0
- {airbyte_cdk-0.58.8.dist-info → airbyte_cdk-0.59.0.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-0.58.8.dist-info → airbyte_cdk-0.59.0.dist-info}/WHEEL +0 -0
- {airbyte_cdk-0.58.8.dist-info → airbyte_cdk-0.59.0.dist-info}/top_level.txt +0 -0
@@ -31,11 +31,12 @@ airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZ
|
|
31
31
|
airbyte_cdk/sources/http_logger.py,sha256=v0kkpDtA0GUOgj6_3AayrYaBrSHBqG4t3MGbrtxaNmU,1437
|
32
32
|
airbyte_cdk/sources/source.py,sha256=dk50z8Roc28MJ8FxWe652B-GwItO__bTZqFm7WOtHnw,4412
|
33
33
|
airbyte_cdk/sources/concurrent_source/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
34
|
-
airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py,sha256=
|
35
|
-
airbyte_cdk/sources/concurrent_source/concurrent_source.py,sha256=
|
34
|
+
airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py,sha256=TIqzf-_fYfCe4n77UwREiXLnFfSlTT-zsHct760KZw8,10052
|
35
|
+
airbyte_cdk/sources/concurrent_source/concurrent_source.py,sha256=Rg8_62UfOIQd1IE8ILpj_z4xNAD9vtIrNRVvMgHMfdA,7957
|
36
36
|
airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py,sha256=si5ipxvzCE2Pdusg19evr8ziG7eqBBuBuDPww6i3Amg,3223
|
37
37
|
airbyte_cdk/sources/concurrent_source/partition_generation_completed_sentinel.py,sha256=oExaUlnDepGZjNmauIkFDCbWtxZvkBCFo1K0wAr4sRA,493
|
38
|
-
airbyte_cdk/sources/concurrent_source/thread_pool_manager.py,sha256=
|
38
|
+
airbyte_cdk/sources/concurrent_source/thread_pool_manager.py,sha256=M-IcXJwMBK31Luk-SLQSFwTV8PelDdaSFMLlB6K1T4w,3908
|
39
|
+
airbyte_cdk/sources/concurrent_source/throttler.py,sha256=gAdwWTk5iveSRUceXIYAms8v1qGSJytzUD07lkR1I9k,926
|
39
40
|
airbyte_cdk/sources/declarative/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
|
40
41
|
airbyte_cdk/sources/declarative/create_partial.py,sha256=sUJOwD8hBzW4pxw2XhYlSTMgl-WMc5WpP5Oq_jo3fHw,3371
|
41
42
|
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=4KtVA54eDmZt8JAFuHLwQv8PmhOmiiaGRBNUgcAQBzQ,87107
|
@@ -201,19 +202,20 @@ airbyte_cdk/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuY
|
|
201
202
|
airbyte_cdk/sources/streams/concurrent/abstract_stream.py,sha256=W7WEz6FrfAjb0o_msnMBIESSVO1qJC2_A8ocYg55Rw4,3579
|
202
203
|
airbyte_cdk/sources/streams/concurrent/adapters.py,sha256=f48kLzOHYNeD7Tfsdy7WaZ__hB24SfCTcW5WpQedqTc,18648
|
203
204
|
airbyte_cdk/sources/streams/concurrent/availability_strategy.py,sha256=8xDRpfktnARBbRi_RwznvKuoGrpPF2b6tQyloMwogkM,2013
|
204
|
-
airbyte_cdk/sources/streams/concurrent/cursor.py,sha256=
|
205
|
+
airbyte_cdk/sources/streams/concurrent/cursor.py,sha256=_mAbnJILeiGOGBNXeeXrSSoz7rveEBMoL97569EPEBY,8106
|
205
206
|
airbyte_cdk/sources/streams/concurrent/default_stream.py,sha256=w83pvFbw9vjfhbovw-LrCFiwQMO8hfo1Vm-1CB1SeXQ,2777
|
206
207
|
airbyte_cdk/sources/streams/concurrent/exceptions.py,sha256=-WETGIY5_QFmVeDFiqm4WhRJ_nNCkfcDwOQqx6cSqrI,365
|
207
|
-
airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py,sha256=
|
208
|
-
airbyte_cdk/sources/streams/concurrent/partition_reader.py,sha256=
|
208
|
+
airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py,sha256=TicEVRyLt5Y85xa8bXsrGzjmNMJAulBLqe1LfBLnHmk,1540
|
209
|
+
airbyte_cdk/sources/streams/concurrent/partition_reader.py,sha256=KHFYK4Yn_6L1gmt31N62K1z28evPKtgI_tkhKQ5oWCg,1358
|
209
210
|
airbyte_cdk/sources/streams/concurrent/partitions/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
210
211
|
airbyte_cdk/sources/streams/concurrent/partitions/partition.py,sha256=o2QvDYZF3Tn9NbC5jc1UkDwMiCWq9fNGj493u2WFoko,1795
|
211
212
|
airbyte_cdk/sources/streams/concurrent/partitions/partition_generator.py,sha256=_ymkkBr71_qt1fW0_MUqw96OfNBkeJngXQ09yolEDHw,441
|
212
213
|
airbyte_cdk/sources/streams/concurrent/partitions/record.py,sha256=-Q3zLex3CHOXiB-KOZLbBZaPiQ_BLFJdknr6yoRz9I0,600
|
214
|
+
airbyte_cdk/sources/streams/concurrent/partitions/throttled_queue.py,sha256=P6KrMb4GtcDUbMcx7pVb7dfF_igeW9Utn2MFoVHkH6o,1589
|
213
215
|
airbyte_cdk/sources/streams/concurrent/partitions/types.py,sha256=iVARnsGOSdvlSCqAf-yxc4_PUT3oOR9B6cyVNcLTjY8,932
|
214
216
|
airbyte_cdk/sources/streams/concurrent/state_converters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
215
|
-
airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py,sha256=
|
216
|
-
airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py,sha256=
|
217
|
+
airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py,sha256=jeVP3V9uDM_aCMh1G3kZNKafjopy1rZzIAJ8sU-69KU,2613
|
218
|
+
airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py,sha256=BvVwPiVHE-R7mWDjcqhsKnZyD6OcpmAYaih7pcmOUvo,7589
|
217
219
|
airbyte_cdk/sources/streams/http/__init__.py,sha256=cTP2d7Wf0hYXaN20U0dtxKa1pFZ9rI-lBbkQ0UM1apQ,261
|
218
220
|
airbyte_cdk/sources/streams/http/availability_strategy.py,sha256=MHgW42gwaevaCVnNLrUSE6WJHT4reeZ417nMWrmbC7o,6884
|
219
221
|
airbyte_cdk/sources/streams/http/exceptions.py,sha256=OokLDI7W8hZvq9e15sL3em2AdwmzmcAl72Ms-i5l0Nw,1334
|
@@ -407,16 +409,18 @@ unit_tests/sources/streams/test_stream_read.py,sha256=xxyYV5jPsAptmI0awPO_VGWMaE
|
|
407
409
|
unit_tests/sources/streams/test_streams_core.py,sha256=YOC7XqWFJ13Z4YuO9Nh4AR4AwpJ-s111vqPplFfpxk4,5059
|
408
410
|
unit_tests/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
409
411
|
unit_tests/sources/streams/concurrent/test_adapters.py,sha256=Y_c1vKCtGKEzrUSncmpgp0lgFnArmBrIrmLFaOIAxRg,15439
|
410
|
-
unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py,sha256=
|
411
|
-
unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py,sha256=
|
412
|
-
unit_tests/sources/streams/concurrent/test_cursor.py,sha256=
|
413
|
-
unit_tests/sources/streams/concurrent/test_datetime_state_converter.py,sha256=
|
412
|
+
unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py,sha256=a5JWWWc20zeEGmMzYzzk-_6XwfDEZOGgW287dVgft_8,1339
|
413
|
+
unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py,sha256=Dc7PGfQge1ymxnaTlPKGMVOLCV81JCXoK1ciJPwHIhg,26347
|
414
|
+
unit_tests/sources/streams/concurrent/test_cursor.py,sha256=0nFp9xauWxDVxJmFQLedvnxWKDoAlk9ChDkd0Mmrhm4,5951
|
415
|
+
unit_tests/sources/streams/concurrent/test_datetime_state_converter.py,sha256=vx-oPmGSzSfBM37ZXN_wXLeTOlozErqQoa5sc5zP42o,13287
|
414
416
|
unit_tests/sources/streams/concurrent/test_default_stream.py,sha256=VLF46ESoRqcoALYCdrdZ2NDl5s2T1fRRWsYAy2-IwYw,6502
|
415
417
|
unit_tests/sources/streams/concurrent/test_partition_reader.py,sha256=2uj7uV3ie0BMb--aa3MUru-f4jLiYUR-Nl0r3EhwxLQ,951
|
416
|
-
unit_tests/sources/streams/concurrent/test_thread_pool_manager.py,sha256=
|
418
|
+
unit_tests/sources/streams/concurrent/test_thread_pool_manager.py,sha256=UzlMhXTgXAuqqPrESGjkDG9JLj4UdPo2bx3T9oLCFpA,3140
|
419
|
+
unit_tests/sources/streams/concurrent/test_throttled_queue.py,sha256=05NgNkx5c5rL8RE2ViOdJz8jty5-67BQPvJNLpVjuMo,1747
|
420
|
+
unit_tests/sources/streams/concurrent/test_throttler.py,sha256=y1cWUdKZP5iy4FKWmGdgEk4e_0WY0VNgRvzmlU114NY,448
|
417
421
|
unit_tests/sources/streams/concurrent/scenarios/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
418
422
|
unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py,sha256=TH4vzdHNWvw4JsF0v4n6wrR1Rnr-WfU3R6nnOwGLNwg,9751
|
419
|
-
unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py,sha256=
|
423
|
+
unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py,sha256=BGqaYgU_ow4PsuhDjFwAFkU1VCkUuromvTAUV5tOaJ8,5816
|
420
424
|
unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py,sha256=kDKKV0ApASyS5c2HYkKvYohSkT--46TqALirqU8POjg,13804
|
421
425
|
unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py,sha256=Z_4-ClsxBupmN7Pbl8lF9bkSA9wnjLtrgA9WR_8VRi8,3757
|
422
426
|
unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py,sha256=KqCLsXB_9rV4hNdSPrNynK3G-UIsipqsZT6X0Z-iM5E,13175
|
@@ -444,8 +448,8 @@ unit_tests/utils/test_schema_inferrer.py,sha256=Z2jHBZ540wnYkylIdV_2xr75Vtwlxuyg
|
|
444
448
|
unit_tests/utils/test_secret_utils.py,sha256=XKe0f1RHYii8iwE6ATmBr5JGDI1pzzrnZUGdUSMJQP4,4886
|
445
449
|
unit_tests/utils/test_stream_status_utils.py,sha256=Xr8MZ2HWgTVIyMbywDvuYkRaUF4RZLQOT8-JjvcfR24,2970
|
446
450
|
unit_tests/utils/test_traced_exception.py,sha256=bDFP5zMBizFenz6V2WvEZTRCKGB5ijh3DBezjbfoYIs,4198
|
447
|
-
airbyte_cdk-0.
|
448
|
-
airbyte_cdk-0.
|
449
|
-
airbyte_cdk-0.
|
450
|
-
airbyte_cdk-0.
|
451
|
-
airbyte_cdk-0.
|
451
|
+
airbyte_cdk-0.59.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
452
|
+
airbyte_cdk-0.59.0.dist-info/METADATA,sha256=5wiAdalwK0NUdE--8UKtiGbRy9ccsaY1T255M_Ei850,11073
|
453
|
+
airbyte_cdk-0.59.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
454
|
+
airbyte_cdk-0.59.0.dist-info/top_level.txt,sha256=edvsDKTnE6sD2wfCUaeTfKf5gQIL6CPVMwVL2sWZzqo,51
|
455
|
+
airbyte_cdk-0.59.0.dist-info/RECORD,,
|
@@ -52,8 +52,7 @@ class StreamFacadeSource(ConcurrentSourceAdapter):
|
|
52
52
|
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
|
53
53
|
state_manager = ConnectorStateManager(stream_instance_map={s.name: s for s in self._streams}, state=self._state)
|
54
54
|
state_converter = StreamFacadeConcurrentConnectorStateConverter()
|
55
|
-
stream_states = [
|
56
|
-
for stream in self._streams]
|
55
|
+
stream_states = [state_manager.get_stream_state(stream.name, stream.namespace) for stream in self._streams]
|
57
56
|
return [
|
58
57
|
StreamFacade.create_from_stream(
|
59
58
|
stream,
|
@@ -69,6 +68,7 @@ class StreamFacadeSource(ConcurrentSourceAdapter):
|
|
69
68
|
state_converter,
|
70
69
|
self._cursor_field,
|
71
70
|
self._cursor_boundaries,
|
71
|
+
None,
|
72
72
|
)
|
73
73
|
if self._cursor_field
|
74
74
|
else NoopCursor(),
|
@@ -2,21 +2,21 @@
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
-
from
|
6
|
-
from unittest.mock import Mock
|
5
|
+
from unittest.mock import Mock, call
|
7
6
|
|
8
7
|
import pytest
|
9
8
|
from airbyte_cdk.models import SyncMode
|
10
9
|
from airbyte_cdk.sources.concurrent_source.partition_generation_completed_sentinel import PartitionGenerationCompletedSentinel
|
11
10
|
from airbyte_cdk.sources.streams.concurrent.adapters import StreamPartition
|
12
11
|
from airbyte_cdk.sources.streams.concurrent.partition_enqueuer import PartitionEnqueuer
|
12
|
+
from airbyte_cdk.sources.streams.concurrent.partitions.throttled_queue import ThrottledQueue
|
13
13
|
|
14
14
|
|
15
15
|
@pytest.mark.parametrize(
|
16
16
|
"slices", [pytest.param([], id="test_no_partitions"), pytest.param([{"partition": 1}, {"partition": 2}], id="test_two_partitions")]
|
17
17
|
)
|
18
18
|
def test_partition_generator(slices):
|
19
|
-
queue =
|
19
|
+
queue = Mock(spec=ThrottledQueue)
|
20
20
|
partition_generator = PartitionEnqueuer(queue)
|
21
21
|
|
22
22
|
stream = Mock()
|
@@ -30,10 +30,4 @@ def test_partition_generator(slices):
|
|
30
30
|
|
31
31
|
partition_generator.generate_partitions(stream)
|
32
32
|
|
33
|
-
|
34
|
-
while partition := queue.get(False):
|
35
|
-
if isinstance(partition, PartitionGenerationCompletedSentinel):
|
36
|
-
break
|
37
|
-
actual_partitions.append(partition)
|
38
|
-
|
39
|
-
assert actual_partitions == partitions
|
33
|
+
assert queue.put.has_calls([call(p) for p in partitions] + [call(PartitionGenerationCompletedSentinel(stream))])
|
@@ -40,13 +40,11 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
40
40
|
self._thread_pool_manager = Mock(spec=ThreadPoolManager)
|
41
41
|
|
42
42
|
self._an_open_partition = Mock(spec=Partition)
|
43
|
-
self._an_open_partition.is_closed.return_value = False
|
44
43
|
self._log_message = Mock(spec=LogMessage)
|
45
44
|
self._an_open_partition.to_slice.return_value = self._log_message
|
46
45
|
self._an_open_partition.stream_name.return_value = _STREAM_NAME
|
47
46
|
|
48
47
|
self._a_closed_partition = Mock(spec=Partition)
|
49
|
-
self._a_closed_partition.is_closed.return_value = True
|
50
48
|
self._a_closed_partition.stream_name.return_value = _ANOTHER_STREAM_NAME
|
51
49
|
|
52
50
|
self._logger = Mock(spec=logging.Logger)
|
@@ -76,6 +74,19 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
76
74
|
self._record.stream_name = _STREAM_NAME
|
77
75
|
self._record.data = self._record_data
|
78
76
|
|
77
|
+
def test_stream_is_not_done_initially(self):
|
78
|
+
stream_instances_to_read_from = [self._stream]
|
79
|
+
handler = ConcurrentReadProcessor(
|
80
|
+
stream_instances_to_read_from,
|
81
|
+
self._partition_enqueuer,
|
82
|
+
self._thread_pool_manager,
|
83
|
+
self._logger,
|
84
|
+
self._slice_logger,
|
85
|
+
self._message_repository,
|
86
|
+
self._partition_reader,
|
87
|
+
)
|
88
|
+
assert not handler._is_stream_done(self._stream.name)
|
89
|
+
|
79
90
|
def test_handle_partition_done_no_other_streams_to_generate_partitions_for(self):
|
80
91
|
stream_instances_to_read_from = [self._stream]
|
81
92
|
|
@@ -111,7 +122,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
111
122
|
self._partition_reader,
|
112
123
|
)
|
113
124
|
handler.start_next_partition_generator()
|
114
|
-
handler.on_partition(self._a_closed_partition)
|
115
125
|
|
116
126
|
sentinel = PartitionGenerationCompletedSentinel(self._another_stream)
|
117
127
|
messages = handler.on_partition_generation_completed(sentinel)
|
@@ -147,7 +157,7 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
147
157
|
handler.on_partition(self._a_closed_partition)
|
148
158
|
|
149
159
|
self._thread_pool_manager.submit.assert_called_with(self._partition_reader.process_partition, self._a_closed_partition)
|
150
|
-
assert self._a_closed_partition in handler.
|
160
|
+
assert self._a_closed_partition in handler._streams_to_running_partitions[_ANOTHER_STREAM_NAME]
|
151
161
|
|
152
162
|
def test_handle_partition_emits_log_message_if_it_should_be_logged(self):
|
153
163
|
stream_instances_to_read_from = [self._stream]
|
@@ -169,15 +179,16 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
169
179
|
|
170
180
|
self._thread_pool_manager.submit.assert_called_with(self._partition_reader.process_partition, self._an_open_partition)
|
171
181
|
self._message_repository.emit_message.assert_called_with(self._log_message)
|
172
|
-
assert self._an_open_partition in handler._streams_to_partitions[_STREAM_NAME]
|
173
182
|
|
183
|
+
assert self._an_open_partition in handler._streams_to_running_partitions[_STREAM_NAME]
|
184
|
+
|
185
|
+
@freezegun.freeze_time("2020-01-01T00:00:00")
|
174
186
|
def test_handle_on_partition_complete_sentinel_with_messages_from_repository(self):
|
175
187
|
stream_instances_to_read_from = [self._stream]
|
176
188
|
partition = Mock(spec=Partition)
|
177
189
|
log_message = Mock(spec=LogMessage)
|
178
190
|
partition.to_slice.return_value = log_message
|
179
191
|
partition.stream_name.return_value = _STREAM_NAME
|
180
|
-
partition.is_closed.return_value = True
|
181
192
|
|
182
193
|
handler = ConcurrentReadProcessor(
|
183
194
|
stream_instances_to_read_from,
|
@@ -189,6 +200,7 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
189
200
|
self._partition_reader,
|
190
201
|
)
|
191
202
|
handler.start_next_partition_generator()
|
203
|
+
handler.on_partition(partition)
|
192
204
|
|
193
205
|
sentinel = PartitionCompleteSentinel(partition)
|
194
206
|
|
@@ -223,6 +235,7 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
223
235
|
self._partition_reader,
|
224
236
|
)
|
225
237
|
handler.start_next_partition_generator()
|
238
|
+
handler.on_partition(self._a_closed_partition)
|
226
239
|
handler.on_partition_generation_completed(PartitionGenerationCompletedSentinel(self._another_stream))
|
227
240
|
|
228
241
|
sentinel = PartitionCompleteSentinel(self._a_closed_partition)
|
@@ -254,7 +267,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
254
267
|
log_message = Mock(spec=LogMessage)
|
255
268
|
partition.to_slice.return_value = log_message
|
256
269
|
partition.stream_name.return_value = _STREAM_NAME
|
257
|
-
partition.is_closed.return_value = True
|
258
270
|
|
259
271
|
handler = ConcurrentReadProcessor(
|
260
272
|
stream_instances_to_read_from,
|
@@ -282,7 +294,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
282
294
|
log_message = Mock(spec=LogMessage)
|
283
295
|
partition.to_slice.return_value = log_message
|
284
296
|
partition.stream_name.return_value = _STREAM_NAME
|
285
|
-
partition.is_closed.return_value = True
|
286
297
|
self._message_repository.consume_queue.return_value = []
|
287
298
|
|
288
299
|
handler = ConcurrentReadProcessor(
|
@@ -319,7 +330,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
319
330
|
log_message = Mock(spec=LogMessage)
|
320
331
|
partition.to_slice.return_value = log_message
|
321
332
|
partition.stream_name.return_value = _STREAM_NAME
|
322
|
-
partition.is_closed.return_value = True
|
323
333
|
slice_logger = Mock(spec=SliceLogger)
|
324
334
|
slice_logger.should_log_slice_message.return_value = True
|
325
335
|
slice_logger.create_slice_log_message.return_value = log_message
|
@@ -370,7 +380,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
370
380
|
stream_instances_to_read_from = [self._stream]
|
371
381
|
partition = Mock(spec=Partition)
|
372
382
|
partition.stream_name.return_value = _STREAM_NAME
|
373
|
-
partition.is_closed.return_value = True
|
374
383
|
|
375
384
|
handler = ConcurrentReadProcessor(
|
376
385
|
stream_instances_to_read_from,
|
@@ -413,7 +422,6 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
413
422
|
log_message = Mock(spec=LogMessage)
|
414
423
|
partition.to_slice.return_value = log_message
|
415
424
|
partition.stream_name.return_value = _STREAM_NAME
|
416
|
-
partition.is_closed.return_value = True
|
417
425
|
self._message_repository.consume_queue.return_value = [
|
418
426
|
AirbyteMessage(type=MessageType.LOG, log=AirbyteLogMessage(level=LogLevel.INFO, message="message emitted from the repository"))
|
419
427
|
]
|
@@ -474,7 +482,69 @@ class TestConcurrentReadProcessor(unittest.TestCase):
|
|
474
482
|
self._message_repository,
|
475
483
|
self._partition_reader,
|
476
484
|
)
|
477
|
-
|
485
|
+
|
486
|
+
handler.start_next_partition_generator()
|
487
|
+
|
488
|
+
another_stream = Mock(spec=AbstractStream)
|
489
|
+
another_stream.name = _STREAM_NAME
|
490
|
+
another_stream.as_airbyte_stream.return_value = AirbyteStream(
|
491
|
+
name=_ANOTHER_STREAM_NAME,
|
492
|
+
json_schema={},
|
493
|
+
supported_sync_modes=[SyncMode.full_refresh],
|
494
|
+
)
|
495
|
+
|
496
|
+
exception = RuntimeError("Something went wrong")
|
497
|
+
|
498
|
+
messages = []
|
499
|
+
|
500
|
+
with self.assertRaises(RuntimeError):
|
501
|
+
for m in handler.on_exception(exception):
|
502
|
+
messages.append(m)
|
503
|
+
|
504
|
+
expected_message = [
|
505
|
+
AirbyteMessage(
|
506
|
+
type=MessageType.TRACE,
|
507
|
+
trace=AirbyteTraceMessage(
|
508
|
+
type=TraceType.STREAM_STATUS,
|
509
|
+
emitted_at=1577836800000.0,
|
510
|
+
stream_status=AirbyteStreamStatusTraceMessage(
|
511
|
+
stream_descriptor=StreamDescriptor(name=_STREAM_NAME), status=AirbyteStreamStatus(AirbyteStreamStatus.INCOMPLETE)
|
512
|
+
),
|
513
|
+
),
|
514
|
+
),
|
515
|
+
AirbyteMessage(
|
516
|
+
type=MessageType.TRACE,
|
517
|
+
trace=AirbyteTraceMessage(
|
518
|
+
type=TraceType.STREAM_STATUS,
|
519
|
+
emitted_at=1577836800000.0,
|
520
|
+
stream_status=AirbyteStreamStatusTraceMessage(
|
521
|
+
stream_descriptor=StreamDescriptor(name=_ANOTHER_STREAM_NAME), status=AirbyteStreamStatus(AirbyteStreamStatus.INCOMPLETE)
|
522
|
+
),
|
523
|
+
),
|
524
|
+
)
|
525
|
+
]
|
526
|
+
|
527
|
+
assert messages == expected_message
|
528
|
+
self._thread_pool_manager.shutdown.assert_called_once()
|
529
|
+
|
530
|
+
@freezegun.freeze_time("2020-01-01T00:00:00")
|
531
|
+
def test_on_exception_does_not_stop_streams_that_are_already_done(self):
|
532
|
+
stream_instances_to_read_from = [self._stream, self._another_stream]
|
533
|
+
|
534
|
+
handler = ConcurrentReadProcessor(
|
535
|
+
stream_instances_to_read_from,
|
536
|
+
self._partition_enqueuer,
|
537
|
+
self._thread_pool_manager,
|
538
|
+
self._logger,
|
539
|
+
self._slice_logger,
|
540
|
+
self._message_repository,
|
541
|
+
self._partition_reader,
|
542
|
+
)
|
543
|
+
|
544
|
+
handler.start_next_partition_generator()
|
545
|
+
handler.on_partition(self._an_open_partition)
|
546
|
+
handler.on_partition_generation_completed(PartitionGenerationCompletedSentinel(self._stream))
|
547
|
+
handler.on_partition_generation_completed(PartitionGenerationCompletedSentinel(self._another_stream))
|
478
548
|
|
479
549
|
another_stream = Mock(spec=AbstractStream)
|
480
550
|
another_stream.name = _STREAM_NAME
|
@@ -45,33 +45,50 @@ class ConcurrentCursorTest(TestCase):
|
|
45
45
|
return ConcurrentCursor(
|
46
46
|
_A_STREAM_NAME,
|
47
47
|
_A_STREAM_NAMESPACE,
|
48
|
-
|
48
|
+
{},
|
49
49
|
self._message_repository,
|
50
50
|
self._state_manager,
|
51
51
|
self._state_converter,
|
52
52
|
CursorField(_A_CURSOR_FIELD_KEY),
|
53
53
|
_SLICE_BOUNDARY_FIELDS,
|
54
|
+
None,
|
54
55
|
)
|
55
56
|
|
56
57
|
def _cursor_without_slice_boundary_fields(self) -> ConcurrentCursor:
|
57
58
|
return ConcurrentCursor(
|
58
59
|
_A_STREAM_NAME,
|
59
60
|
_A_STREAM_NAMESPACE,
|
60
|
-
|
61
|
+
{},
|
61
62
|
self._message_repository,
|
62
63
|
self._state_manager,
|
63
64
|
self._state_converter,
|
64
65
|
CursorField(_A_CURSOR_FIELD_KEY),
|
65
66
|
None,
|
67
|
+
None,
|
66
68
|
)
|
67
69
|
|
68
70
|
def test_given_boundary_fields_when_close_partition_then_emit_state(self) -> None:
|
69
|
-
self._cursor_with_slice_boundary_fields()
|
71
|
+
cursor = self._cursor_with_slice_boundary_fields()
|
72
|
+
cursor.close_partition(
|
70
73
|
_partition(
|
71
74
|
{_LOWER_SLICE_BOUNDARY_FIELD: 12, _UPPER_SLICE_BOUNDARY_FIELD: 30},
|
72
75
|
)
|
73
76
|
)
|
74
77
|
|
78
|
+
self._message_repository.emit_message.assert_called_once_with(self._state_manager.create_state_message.return_value)
|
79
|
+
self._state_manager.update_state_for_stream.assert_called_once_with(
|
80
|
+
_A_STREAM_NAME,
|
81
|
+
_A_STREAM_NAMESPACE,
|
82
|
+
{_A_CURSOR_FIELD_KEY: 0}, # State message is updated to the legacy format before being emitted
|
83
|
+
)
|
84
|
+
|
85
|
+
def test_given_boundary_fields_when_close_partition_then_emit_updated_state(self) -> None:
|
86
|
+
self._cursor_with_slice_boundary_fields().close_partition(
|
87
|
+
_partition(
|
88
|
+
{_LOWER_SLICE_BOUNDARY_FIELD: 0, _UPPER_SLICE_BOUNDARY_FIELD: 30},
|
89
|
+
)
|
90
|
+
)
|
91
|
+
|
75
92
|
self._message_repository.emit_message.assert_called_once_with(self._state_manager.create_state_message.return_value)
|
76
93
|
self._state_manager.update_state_for_stream.assert_called_once_with(
|
77
94
|
_A_STREAM_NAME,
|