airbyte-cdk 0.58.8__py3-none-any.whl → 0.59.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,
|