airbyte-cdk 0.54.0__py3-none-any.whl → 0.55.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/__init__.py +3 -0
- airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +190 -0
- airbyte_cdk/sources/concurrent_source/concurrent_source.py +161 -0
- airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py +63 -0
- airbyte_cdk/sources/concurrent_source/partition_generation_completed_sentinel.py +17 -0
- airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +97 -0
- airbyte_cdk/sources/streams/concurrent/abstract_stream.py +4 -4
- airbyte_cdk/sources/streams/concurrent/adapters.py +34 -12
- airbyte_cdk/sources/streams/concurrent/default_stream.py +79 -0
- airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +7 -7
- airbyte_cdk/sources/streams/concurrent/partitions/partition.py +23 -0
- airbyte_cdk/sources/streams/concurrent/partitions/record.py +4 -3
- airbyte_cdk/sources/streams/concurrent/partitions/types.py +2 -3
- airbyte_cdk/sources/utils/slice_logger.py +5 -0
- {airbyte_cdk-0.54.0.dist-info → airbyte_cdk-0.55.0.dist-info}/METADATA +1 -1
- {airbyte_cdk-0.54.0.dist-info → airbyte_cdk-0.55.0.dist-info}/RECORD +35 -23
- unit_tests/sources/concurrent_source/__init__.py +3 -0
- unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py +105 -0
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py +14 -7
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py +2 -3
- unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py +44 -55
- unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py +24 -15
- unit_tests/sources/streams/concurrent/test_adapters.py +52 -32
- unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py +6 -5
- unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py +604 -0
- unit_tests/sources/streams/concurrent/test_cursor.py +1 -1
- unit_tests/sources/streams/concurrent/{test_thread_based_concurrent_stream.py → test_default_stream.py} +7 -144
- unit_tests/sources/streams/concurrent/test_partition_reader.py +2 -2
- unit_tests/sources/streams/concurrent/test_thread_pool_manager.py +98 -0
- unit_tests/sources/streams/test_stream_read.py +1 -2
- unit_tests/sources/test_concurrent_source.py +105 -0
- unit_tests/sources/test_source_read.py +461 -0
- airbyte_cdk/sources/streams/concurrent/thread_based_concurrent_stream.py +0 -221
- {airbyte_cdk-0.54.0.dist-info → airbyte_cdk-0.55.0.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-0.54.0.dist-info → airbyte_cdk-0.55.0.dist-info}/WHEEL +0 -0
- {airbyte_cdk-0.54.0.dist-info → airbyte_cdk-0.55.0.dist-info}/top_level.txt +0 -0
@@ -22,11 +22,11 @@ from airbyte_cdk.sources.streams.concurrent.availability_strategy import (
|
|
22
22
|
StreamUnavailable,
|
23
23
|
)
|
24
24
|
from airbyte_cdk.sources.streams.concurrent.cursor import Cursor, NoopCursor
|
25
|
+
from airbyte_cdk.sources.streams.concurrent.default_stream import DefaultStream
|
25
26
|
from airbyte_cdk.sources.streams.concurrent.exceptions import ExceptionWithDisplayMessage
|
26
27
|
from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
|
27
28
|
from airbyte_cdk.sources.streams.concurrent.partitions.partition_generator import PartitionGenerator
|
28
29
|
from airbyte_cdk.sources.streams.concurrent.partitions.record import Record
|
29
|
-
from airbyte_cdk.sources.streams.concurrent.thread_based_concurrent_stream import ThreadBasedConcurrentStream
|
30
30
|
from airbyte_cdk.sources.streams.core import StreamData
|
31
31
|
from airbyte_cdk.sources.utils.schema_helpers import InternalConfig
|
32
32
|
from airbyte_cdk.sources.utils.slice_logger import SliceLogger
|
@@ -52,7 +52,6 @@ class StreamFacade(Stream):
|
|
52
52
|
stream: Stream,
|
53
53
|
source: AbstractSource,
|
54
54
|
logger: logging.Logger,
|
55
|
-
max_workers: int,
|
56
55
|
state: Optional[MutableMapping[str, Any]],
|
57
56
|
cursor: Cursor,
|
58
57
|
) -> Stream:
|
@@ -73,28 +72,27 @@ class StreamFacade(Stream):
|
|
73
72
|
|
74
73
|
message_repository = source.message_repository
|
75
74
|
return StreamFacade(
|
76
|
-
|
75
|
+
DefaultStream(
|
77
76
|
partition_generator=StreamPartitionGenerator(
|
78
77
|
stream,
|
79
78
|
message_repository,
|
80
79
|
SyncMode.full_refresh if isinstance(cursor, NoopCursor) else SyncMode.incremental,
|
81
80
|
[cursor_field] if cursor_field is not None else None,
|
82
81
|
state,
|
82
|
+
cursor,
|
83
83
|
),
|
84
|
-
max_workers=max_workers,
|
85
84
|
name=stream.name,
|
86
85
|
namespace=stream.namespace,
|
87
86
|
json_schema=stream.get_json_schema(),
|
88
87
|
availability_strategy=StreamAvailabilityStrategy(stream, source),
|
89
88
|
primary_key=pk,
|
90
89
|
cursor_field=cursor_field,
|
91
|
-
slice_logger=source._slice_logger,
|
92
|
-
message_repository=message_repository,
|
93
90
|
logger=logger,
|
94
|
-
cursor=cursor,
|
95
91
|
),
|
96
92
|
stream,
|
97
93
|
cursor,
|
94
|
+
slice_logger=source._slice_logger,
|
95
|
+
logger=logger,
|
98
96
|
)
|
99
97
|
|
100
98
|
@property
|
@@ -132,13 +130,15 @@ class StreamFacade(Stream):
|
|
132
130
|
else:
|
133
131
|
return stream.cursor_field
|
134
132
|
|
135
|
-
def __init__(self, stream: AbstractStream, legacy_stream: Stream, cursor: Cursor):
|
133
|
+
def __init__(self, stream: AbstractStream, legacy_stream: Stream, cursor: Cursor, slice_logger: SliceLogger, logger: logging.Logger):
|
136
134
|
"""
|
137
135
|
:param stream: The underlying AbstractStream
|
138
136
|
"""
|
139
137
|
self._abstract_stream = stream
|
140
138
|
self._legacy_stream = legacy_stream
|
141
139
|
self._cursor = cursor
|
140
|
+
self._slice_logger = slice_logger
|
141
|
+
self._logger = logger
|
142
142
|
|
143
143
|
def read_full_refresh(
|
144
144
|
self,
|
@@ -177,8 +177,11 @@ class StreamFacade(Stream):
|
|
177
177
|
yield from self._read_records()
|
178
178
|
|
179
179
|
def _read_records(self) -> Iterable[StreamData]:
|
180
|
-
for
|
181
|
-
|
180
|
+
for partition in self._abstract_stream.generate_partitions():
|
181
|
+
if self._slice_logger.should_log_slice_message(self._logger):
|
182
|
+
yield self._slice_logger.create_slice_log_message(partition.to_slice())
|
183
|
+
for record in partition.read():
|
184
|
+
yield record.data
|
182
185
|
|
183
186
|
@property
|
184
187
|
def name(self) -> str:
|
@@ -259,6 +262,7 @@ class StreamPartition(Partition):
|
|
259
262
|
sync_mode: SyncMode,
|
260
263
|
cursor_field: Optional[List[str]],
|
261
264
|
state: Optional[MutableMapping[str, Any]],
|
265
|
+
cursor: Cursor,
|
262
266
|
):
|
263
267
|
"""
|
264
268
|
:param stream: The stream to delegate to
|
@@ -271,6 +275,8 @@ class StreamPartition(Partition):
|
|
271
275
|
self._sync_mode = sync_mode
|
272
276
|
self._cursor_field = cursor_field
|
273
277
|
self._state = state
|
278
|
+
self._cursor = cursor
|
279
|
+
self._is_closed = False
|
274
280
|
|
275
281
|
def read(self) -> Iterable[Record]:
|
276
282
|
"""
|
@@ -294,7 +300,9 @@ class StreamPartition(Partition):
|
|
294
300
|
if isinstance(record_data, Mapping):
|
295
301
|
data_to_return = dict(record_data)
|
296
302
|
self._stream.transformer.transform(data_to_return, self._stream.get_json_schema())
|
297
|
-
|
303
|
+
record = Record(data_to_return, self._stream.name)
|
304
|
+
self._cursor.observe(record)
|
305
|
+
yield Record(data_to_return, self._stream.name)
|
298
306
|
else:
|
299
307
|
self._message_repository.emit_message(record_data)
|
300
308
|
except Exception as e:
|
@@ -315,6 +323,16 @@ class StreamPartition(Partition):
|
|
315
323
|
else:
|
316
324
|
return hash(self._stream.name)
|
317
325
|
|
326
|
+
def stream_name(self) -> str:
|
327
|
+
return self._stream.name
|
328
|
+
|
329
|
+
def close(self) -> None:
|
330
|
+
self._cursor.close_partition(self)
|
331
|
+
self._is_closed = True
|
332
|
+
|
333
|
+
def is_closed(self) -> bool:
|
334
|
+
return self._is_closed
|
335
|
+
|
318
336
|
def __repr__(self) -> str:
|
319
337
|
return f"StreamPartition({self._stream.name}, {self._slice})"
|
320
338
|
|
@@ -334,6 +352,7 @@ class StreamPartitionGenerator(PartitionGenerator):
|
|
334
352
|
sync_mode: SyncMode,
|
335
353
|
cursor_field: Optional[List[str]],
|
336
354
|
state: Optional[MutableMapping[str, Any]],
|
355
|
+
cursor: Cursor,
|
337
356
|
):
|
338
357
|
"""
|
339
358
|
:param stream: The stream to delegate to
|
@@ -344,10 +363,13 @@ class StreamPartitionGenerator(PartitionGenerator):
|
|
344
363
|
self._sync_mode = sync_mode
|
345
364
|
self._cursor_field = cursor_field
|
346
365
|
self._state = state
|
366
|
+
self._cursor = cursor
|
347
367
|
|
348
368
|
def generate(self) -> Iterable[Partition]:
|
349
369
|
for s in self._stream.stream_slices(sync_mode=self._sync_mode, cursor_field=self._cursor_field, stream_state=self._state):
|
350
|
-
yield StreamPartition(
|
370
|
+
yield StreamPartition(
|
371
|
+
self._stream, copy.deepcopy(s), self.message_repository, self._sync_mode, self._cursor_field, self._state, self._cursor
|
372
|
+
)
|
351
373
|
|
352
374
|
|
353
375
|
@deprecated("This class is experimental. Use at your own risk.")
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
from functools import lru_cache
|
6
|
+
from logging import Logger
|
7
|
+
from typing import Any, Iterable, List, Mapping, Optional
|
8
|
+
|
9
|
+
from airbyte_cdk.models import AirbyteStream, SyncMode
|
10
|
+
from airbyte_cdk.sources.streams.concurrent.abstract_stream import AbstractStream
|
11
|
+
from airbyte_cdk.sources.streams.concurrent.availability_strategy import AbstractAvailabilityStrategy, StreamAvailability
|
12
|
+
from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
|
13
|
+
from airbyte_cdk.sources.streams.concurrent.partitions.partition_generator import PartitionGenerator
|
14
|
+
|
15
|
+
|
16
|
+
class DefaultStream(AbstractStream):
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
partition_generator: PartitionGenerator,
|
20
|
+
name: str,
|
21
|
+
json_schema: Mapping[str, Any],
|
22
|
+
availability_strategy: AbstractAvailabilityStrategy,
|
23
|
+
primary_key: List[str],
|
24
|
+
cursor_field: Optional[str],
|
25
|
+
logger: Logger,
|
26
|
+
namespace: Optional[str] = None,
|
27
|
+
) -> None:
|
28
|
+
self._stream_partition_generator = partition_generator
|
29
|
+
self._name = name
|
30
|
+
self._json_schema = json_schema
|
31
|
+
self._availability_strategy = availability_strategy
|
32
|
+
self._primary_key = primary_key
|
33
|
+
self._cursor_field = cursor_field
|
34
|
+
self._logger = logger
|
35
|
+
self._namespace = namespace
|
36
|
+
|
37
|
+
def generate_partitions(self) -> Iterable[Partition]:
|
38
|
+
yield from self._stream_partition_generator.generate()
|
39
|
+
|
40
|
+
@property
|
41
|
+
def name(self) -> str:
|
42
|
+
return self._name
|
43
|
+
|
44
|
+
def check_availability(self) -> StreamAvailability:
|
45
|
+
return self._availability_strategy.check_availability(self._logger)
|
46
|
+
|
47
|
+
@property
|
48
|
+
def cursor_field(self) -> Optional[str]:
|
49
|
+
return self._cursor_field
|
50
|
+
|
51
|
+
@lru_cache(maxsize=None)
|
52
|
+
def get_json_schema(self) -> Mapping[str, Any]:
|
53
|
+
return self._json_schema
|
54
|
+
|
55
|
+
def as_airbyte_stream(self) -> AirbyteStream:
|
56
|
+
stream = AirbyteStream(name=self.name, json_schema=dict(self._json_schema), supported_sync_modes=[SyncMode.full_refresh])
|
57
|
+
|
58
|
+
if self._namespace:
|
59
|
+
stream.namespace = self._namespace
|
60
|
+
|
61
|
+
if self._cursor_field:
|
62
|
+
stream.source_defined_cursor = True
|
63
|
+
stream.supported_sync_modes.append(SyncMode.incremental)
|
64
|
+
stream.default_cursor_field = [self._cursor_field]
|
65
|
+
|
66
|
+
keys = self._primary_key
|
67
|
+
if keys and len(keys) > 0:
|
68
|
+
stream.source_defined_primary_key = [keys]
|
69
|
+
|
70
|
+
return stream
|
71
|
+
|
72
|
+
def log_stream_sync_configuration(self) -> None:
|
73
|
+
self._logger.debug(
|
74
|
+
f"Syncing stream instance: {self.name}",
|
75
|
+
extra={
|
76
|
+
"primary_key": self._primary_key,
|
77
|
+
"cursor_field": self.cursor_field,
|
78
|
+
},
|
79
|
+
)
|
@@ -4,8 +4,9 @@
|
|
4
4
|
|
5
5
|
from queue import Queue
|
6
6
|
|
7
|
-
from airbyte_cdk.sources.
|
8
|
-
from airbyte_cdk.sources.streams.concurrent.
|
7
|
+
from airbyte_cdk.sources.concurrent_source.partition_generation_completed_sentinel import PartitionGenerationCompletedSentinel
|
8
|
+
from airbyte_cdk.sources.streams.concurrent.abstract_stream import AbstractStream
|
9
|
+
from airbyte_cdk.sources.streams.concurrent.partitions.types import QueueItem
|
9
10
|
|
10
11
|
|
11
12
|
class PartitionEnqueuer:
|
@@ -13,15 +14,14 @@ class PartitionEnqueuer:
|
|
13
14
|
Generates partitions from a partition generator and puts them in a queue.
|
14
15
|
"""
|
15
16
|
|
16
|
-
def __init__(self, queue: Queue[QueueItem]
|
17
|
+
def __init__(self, queue: Queue[QueueItem]) -> None:
|
17
18
|
"""
|
18
19
|
:param queue: The queue to put the partitions in.
|
19
20
|
:param sentinel: The sentinel to put in the queue when all the partitions have been generated.
|
20
21
|
"""
|
21
22
|
self._queue = queue
|
22
|
-
self._sentinel = sentinel
|
23
23
|
|
24
|
-
def generate_partitions(self,
|
24
|
+
def generate_partitions(self, stream: AbstractStream) -> None:
|
25
25
|
"""
|
26
26
|
Generate partitions from a partition generator and put them in a queue.
|
27
27
|
When all the partitions are added to the queue, a sentinel is added to the queue to indicate that all the partitions have been generated.
|
@@ -33,8 +33,8 @@ class PartitionEnqueuer:
|
|
33
33
|
:return:
|
34
34
|
"""
|
35
35
|
try:
|
36
|
-
for partition in
|
36
|
+
for partition in stream.generate_partitions():
|
37
37
|
self._queue.put(partition)
|
38
|
-
self._queue.put(
|
38
|
+
self._queue.put(PartitionGenerationCompletedSentinel(stream))
|
39
39
|
except Exception as e:
|
40
40
|
self._queue.put(e)
|
@@ -32,6 +32,29 @@ class Partition(ABC):
|
|
32
32
|
"""
|
33
33
|
pass
|
34
34
|
|
35
|
+
@abstractmethod
|
36
|
+
def stream_name(self) -> str:
|
37
|
+
"""
|
38
|
+
Returns the name of the stream that this partition is reading from.
|
39
|
+
:return: The name of the stream.
|
40
|
+
"""
|
41
|
+
pass
|
42
|
+
|
43
|
+
@abstractmethod
|
44
|
+
def close(self) -> None:
|
45
|
+
"""
|
46
|
+
Closes the partition.
|
47
|
+
"""
|
48
|
+
pass
|
49
|
+
|
50
|
+
@abstractmethod
|
51
|
+
def is_closed(self) -> bool:
|
52
|
+
"""
|
53
|
+
Returns whether the partition is closed.
|
54
|
+
:return:
|
55
|
+
"""
|
56
|
+
pass
|
57
|
+
|
35
58
|
@abstractmethod
|
36
59
|
def __hash__(self) -> int:
|
37
60
|
"""
|
@@ -10,13 +10,14 @@ class Record:
|
|
10
10
|
Represents a record read from a stream.
|
11
11
|
"""
|
12
12
|
|
13
|
-
def __init__(self, data: Mapping[str, Any]):
|
13
|
+
def __init__(self, data: Mapping[str, Any], stream_name: str):
|
14
14
|
self.data = data
|
15
|
+
self.stream_name = stream_name
|
15
16
|
|
16
17
|
def __eq__(self, other: Any) -> bool:
|
17
18
|
if not isinstance(other, Record):
|
18
19
|
return False
|
19
|
-
return self.data == other.data
|
20
|
+
return self.data == other.data and self.stream_name == other.stream_name
|
20
21
|
|
21
22
|
def __repr__(self) -> str:
|
22
|
-
return f"Record(data={self.data})"
|
23
|
+
return f"Record(data={self.data}, stream_name={self.stream_name})"
|
@@ -4,11 +4,10 @@
|
|
4
4
|
|
5
5
|
from typing import Union
|
6
6
|
|
7
|
+
from airbyte_cdk.sources.concurrent_source.partition_generation_completed_sentinel import PartitionGenerationCompletedSentinel
|
7
8
|
from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
|
8
9
|
from airbyte_cdk.sources.streams.concurrent.partitions.record import Record
|
9
10
|
|
10
|
-
PARTITIONS_GENERATED_SENTINEL = object
|
11
|
-
|
12
11
|
|
13
12
|
class PartitionCompleteSentinel:
|
14
13
|
"""
|
@@ -26,4 +25,4 @@ class PartitionCompleteSentinel:
|
|
26
25
|
"""
|
27
26
|
Typedef representing the items that can be added to the ThreadBasedConcurrentStream
|
28
27
|
"""
|
29
|
-
QueueItem = Union[Record, Partition, PartitionCompleteSentinel,
|
28
|
+
QueueItem = Union[Record, Partition, PartitionCompleteSentinel, PartitionGenerationCompletedSentinel, Exception]
|
@@ -12,6 +12,11 @@ from airbyte_cdk.models import Type as MessageType
|
|
12
12
|
|
13
13
|
|
14
14
|
class SliceLogger(ABC):
|
15
|
+
"""
|
16
|
+
SliceLogger is an interface that allows us to log slices of data in a uniform way.
|
17
|
+
It is responsible for determining whether or not a slice should be logged and for creating the log message.
|
18
|
+
"""
|
19
|
+
|
15
20
|
SLICE_LOG_PREFIX = "slice:"
|
16
21
|
|
17
22
|
def create_slice_log_message(self, _slice: Optional[Mapping[str, Any]]) -> AirbyteMessage:
|
@@ -30,6 +30,12 @@ airbyte_cdk/sources/connector_state_manager.py,sha256=wsmUgII398MazCTKxwLBLzeiU6
|
|
30
30
|
airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZujIS0,730
|
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
|
+
airbyte_cdk/sources/concurrent_source/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
34
|
+
airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py,sha256=7A8bdOqg9Hpw37QgqoGO6eYBx8_pKikt_AJUcImp-x4,9715
|
35
|
+
airbyte_cdk/sources/concurrent_source/concurrent_source.py,sha256=nmHZLmUwHYW5a8B-XpwVLZ2weys5oWTVYSMseW7WdYc,7836
|
36
|
+
airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py,sha256=si5ipxvzCE2Pdusg19evr8ziG7eqBBuBuDPww6i3Amg,3223
|
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=huFp0uuG2kZAnbHY8oeDYuX0hfmP-rLOnJMa72ZuWt0,3905
|
33
39
|
airbyte_cdk/sources/declarative/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
|
34
40
|
airbyte_cdk/sources/declarative/create_partial.py,sha256=sUJOwD8hBzW4pxw2XhYlSTMgl-WMc5WpP5Oq_jo3fHw,3371
|
35
41
|
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=LtLvEpzKo86RzMO6n20-z4ECW6P0Yoi26HXRCSLP9K0,85049
|
@@ -191,20 +197,20 @@ airbyte_cdk/sources/streams/availability_strategy.py,sha256=7BM0qLvXS0QrlKvnVkBE
|
|
191
197
|
airbyte_cdk/sources/streams/call_rate.py,sha256=5T4J8WxMNov76iXRUtD5KlM1CsROxuAQPwGQAZyvpHg,20555
|
192
198
|
airbyte_cdk/sources/streams/core.py,sha256=bIuQV7Zs9JpIyNDcfPCbyzv-BWDr_2ictK7s5AihLZQ,16025
|
193
199
|
airbyte_cdk/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
194
|
-
airbyte_cdk/sources/streams/concurrent/abstract_stream.py,sha256=
|
195
|
-
airbyte_cdk/sources/streams/concurrent/adapters.py,sha256=
|
200
|
+
airbyte_cdk/sources/streams/concurrent/abstract_stream.py,sha256=W7WEz6FrfAjb0o_msnMBIESSVO1qJC2_A8ocYg55Rw4,3579
|
201
|
+
airbyte_cdk/sources/streams/concurrent/adapters.py,sha256=kBlHLeHi_xg_rtPMzfyU4Osh-k7K5NLUMOMfAPwiwrk,18090
|
196
202
|
airbyte_cdk/sources/streams/concurrent/availability_strategy.py,sha256=8xDRpfktnARBbRi_RwznvKuoGrpPF2b6tQyloMwogkM,2013
|
197
203
|
airbyte_cdk/sources/streams/concurrent/cursor.py,sha256=vvQeY-IkJ8gfwKfCZTLTVlBYA9gVgQX6bYdWUT0D-q4,6504
|
204
|
+
airbyte_cdk/sources/streams/concurrent/default_stream.py,sha256=w83pvFbw9vjfhbovw-LrCFiwQMO8hfo1Vm-1CB1SeXQ,2777
|
198
205
|
airbyte_cdk/sources/streams/concurrent/exceptions.py,sha256=-WETGIY5_QFmVeDFiqm4WhRJ_nNCkfcDwOQqx6cSqrI,365
|
199
|
-
airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py,sha256=
|
206
|
+
airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py,sha256=Mmn0hYq2xWe2a0WOpZPF3iZNozfmv7vY37LgfdY7DVo,1570
|
200
207
|
airbyte_cdk/sources/streams/concurrent/partition_reader.py,sha256=H8sGVVGx6uKMSUehRaqmVbE19DE3cx3NivQ4sFj8wbk,1303
|
201
208
|
airbyte_cdk/sources/streams/concurrent/state_converter.py,sha256=PwqcRVPR6LQxWL0yvPTp_u2Uh0hBJU-BDSjPKiyJVEk,4689
|
202
|
-
airbyte_cdk/sources/streams/concurrent/thread_based_concurrent_stream.py,sha256=M7CpPPBswHTYjG4opiTOf5eWHOJ6i4TyP0v991pFxOo,10843
|
203
209
|
airbyte_cdk/sources/streams/concurrent/partitions/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
204
|
-
airbyte_cdk/sources/streams/concurrent/partitions/partition.py,sha256=
|
210
|
+
airbyte_cdk/sources/streams/concurrent/partitions/partition.py,sha256=o2QvDYZF3Tn9NbC5jc1UkDwMiCWq9fNGj493u2WFoko,1795
|
205
211
|
airbyte_cdk/sources/streams/concurrent/partitions/partition_generator.py,sha256=_ymkkBr71_qt1fW0_MUqw96OfNBkeJngXQ09yolEDHw,441
|
206
|
-
airbyte_cdk/sources/streams/concurrent/partitions/record.py,sha256
|
207
|
-
airbyte_cdk/sources/streams/concurrent/partitions/types.py,sha256=
|
212
|
+
airbyte_cdk/sources/streams/concurrent/partitions/record.py,sha256=-Q3zLex3CHOXiB-KOZLbBZaPiQ_BLFJdknr6yoRz9I0,600
|
213
|
+
airbyte_cdk/sources/streams/concurrent/partitions/types.py,sha256=iVARnsGOSdvlSCqAf-yxc4_PUT3oOR9B6cyVNcLTjY8,932
|
208
214
|
airbyte_cdk/sources/streams/http/__init__.py,sha256=cTP2d7Wf0hYXaN20U0dtxKa1pFZ9rI-lBbkQ0UM1apQ,261
|
209
215
|
airbyte_cdk/sources/streams/http/availability_strategy.py,sha256=MHgW42gwaevaCVnNLrUSE6WJHT4reeZ417nMWrmbC7o,6884
|
210
216
|
airbyte_cdk/sources/streams/http/exceptions.py,sha256=OokLDI7W8hZvq9e15sL3em2AdwmzmcAl72Ms-i5l0Nw,1334
|
@@ -227,7 +233,7 @@ airbyte_cdk/sources/utils/catalog_helpers.py,sha256=Jo3F6NQE2O7aP4x7yGScwbvtPQyC
|
|
227
233
|
airbyte_cdk/sources/utils/record_helper.py,sha256=lNtOK1rMUxB9cw6wIi3yNu85jlqTN5inTkZZCWvPKXA,1711
|
228
234
|
airbyte_cdk/sources/utils/schema_helpers.py,sha256=_Kasvdo60OE1aHkrd2Q48OHrMJnZ8nSWliuAVbR7vJs,8483
|
229
235
|
airbyte_cdk/sources/utils/schema_models.py,sha256=m1vOqNkkVYGblc492wKo11Zm5FK9F0-JoNb50aRZnew,3151
|
230
|
-
airbyte_cdk/sources/utils/slice_logger.py,sha256=
|
236
|
+
airbyte_cdk/sources/utils/slice_logger.py,sha256=YeWSoZeOsQp9oZK7mick2J8KFdiY726LY2iiIj_--r4,1731
|
231
237
|
airbyte_cdk/sources/utils/transform.py,sha256=4GYmO6bq33HF-a1in0dKQKqUOYI1bWItyuYF875bSQg,9493
|
232
238
|
airbyte_cdk/sources/utils/types.py,sha256=41ZQR681t5TUnOScij58d088sb99klH_ZENFcaYro_g,175
|
233
239
|
airbyte_cdk/utils/__init__.py,sha256=qZoNqzEKhIXdN_ZfvXlIGnmiDDjCFy6BVCzzWjUZcuU,294
|
@@ -256,11 +262,15 @@ unit_tests/singer/test_singer_helpers.py,sha256=pZV6VxJuK-3-FICNGmoGbokrA_zkaFZE
|
|
256
262
|
unit_tests/singer/test_singer_source.py,sha256=edN_kv7dnYAdBveWdUYOs74ak0dK6p8uaX225h_ZILA,4442
|
257
263
|
unit_tests/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
258
264
|
unit_tests/sources/test_abstract_source.py,sha256=V7zSpOk-jqfOz8FtnImAo_zDe-Q2TjPqD_l_T0QaiDw,48179
|
265
|
+
unit_tests/sources/test_concurrent_source.py,sha256=NT4K0z-oz2OZBHE9xNQT0KUdI2wJ-5vNWLUHZlIYKKU,3552
|
259
266
|
unit_tests/sources/test_config.py,sha256=lxjeaf48pOMF4Pf3-Z1ux_tHTyjRFCdG_hpnxw3e7uQ,2839
|
260
267
|
unit_tests/sources/test_connector_state_manager.py,sha256=ynFxA63Cxe6t-wMMh9C6ByTlMAuk8W7H2FikDhnUEQ0,24264
|
261
268
|
unit_tests/sources/test_http_logger.py,sha256=VT6DqgspI3DcRnoBQkkQX0z4dF_AOiYZ5P_zxmMW8oU,9004
|
262
269
|
unit_tests/sources/test_integration_source.py,sha256=7DAWzuYwU_HXzhw-rRjjwQuQej-hVpNyzw_NLqQiJVc,3369
|
263
270
|
unit_tests/sources/test_source.py,sha256=W0I4umL_d_OToLYYiRkjkJR6e-cCYjdV8zKc3uLvF0k,27999
|
271
|
+
unit_tests/sources/test_source_read.py,sha256=AEFoJfzM0_5QQIJyKwGLK_kq_Vz_CBivImnUnXJQJ0I,17176
|
272
|
+
unit_tests/sources/concurrent_source/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
273
|
+
unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py,sha256=zsGnMcEsBedjW8wahil6LNqniil-3NXhyZd5W-80Km0,3665
|
264
274
|
unit_tests/sources/declarative/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
|
265
275
|
unit_tests/sources/declarative/external_component.py,sha256=lU2gL736bLEWtmrGm1B2k83RXt_3XkROimLIahZd5dg,293
|
266
276
|
unit_tests/sources/declarative/test_create_partial.py,sha256=s_KIywQqt8RlauOCWNJVk3HC3KBTAtSwFTN6JVQgu80,2636
|
@@ -379,22 +389,24 @@ unit_tests/sources/message/test_repository.py,sha256=oiScwg4cAdnYDl7PPN1nZniDGpA
|
|
379
389
|
unit_tests/sources/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
380
390
|
unit_tests/sources/streams/test_availability_strategy.py,sha256=vJrSEk9NwRghu0YsSNoMYHKWzA9UFemwyClpke8Mk2s,2315
|
381
391
|
unit_tests/sources/streams/test_call_rate.py,sha256=5QsokqxIFoR438QTd7p_eb0K-LW6awZXDtQiMTAb_Qo,13069
|
382
|
-
unit_tests/sources/streams/test_stream_read.py,sha256=
|
392
|
+
unit_tests/sources/streams/test_stream_read.py,sha256=xxyYV5jPsAptmI0awPO_VGWMaE-y80XMDCB6u87IPaY,6875
|
383
393
|
unit_tests/sources/streams/test_streams_core.py,sha256=YOC7XqWFJ13Z4YuO9Nh4AR4AwpJ-s111vqPplFfpxk4,5059
|
384
394
|
unit_tests/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
385
|
-
unit_tests/sources/streams/concurrent/test_adapters.py,sha256=
|
386
|
-
unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py,sha256=
|
387
|
-
unit_tests/sources/streams/concurrent/
|
388
|
-
unit_tests/sources/streams/concurrent/
|
395
|
+
unit_tests/sources/streams/concurrent/test_adapters.py,sha256=Y_c1vKCtGKEzrUSncmpgp0lgFnArmBrIrmLFaOIAxRg,15439
|
396
|
+
unit_tests/sources/streams/concurrent/test_concurrent_partition_generator.py,sha256=v8yf19_sDgVcWop6WKotahlQiO6B8MwxhGi3AL4vHm8,1375
|
397
|
+
unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py,sha256=1578s0CtFVQALrv2slo12RIgNmNwwobJAFWfBre8jdc,23822
|
398
|
+
unit_tests/sources/streams/concurrent/test_cursor.py,sha256=xsQ0zHKzU-iRnpTiAMvGRPbiL50zRJerDoloenkhcj0,5818
|
399
|
+
unit_tests/sources/streams/concurrent/test_default_stream.py,sha256=VLF46ESoRqcoALYCdrdZ2NDl5s2T1fRRWsYAy2-IwYw,6502
|
400
|
+
unit_tests/sources/streams/concurrent/test_partition_reader.py,sha256=2uj7uV3ie0BMb--aa3MUru-f4jLiYUR-Nl0r3EhwxLQ,951
|
389
401
|
unit_tests/sources/streams/concurrent/test_state_converter.py,sha256=rvg8becWR1iPdm5TAanZssKj5_iw8dInE_uqmjqghZE,8349
|
390
|
-
unit_tests/sources/streams/concurrent/
|
402
|
+
unit_tests/sources/streams/concurrent/test_thread_pool_manager.py,sha256=7L9Sv7VXULOHx3-KSFwFtzAY1X96wcPiPKGq38BQEVg,3699
|
391
403
|
unit_tests/sources/streams/concurrent/scenarios/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
392
404
|
unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py,sha256=x77AQf8_O4dQ2aF1o800CzI0hOEyU8ayxoNdSOvxkhM,10495
|
393
|
-
unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py,sha256=
|
394
|
-
unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py,sha256=
|
405
|
+
unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py,sha256=OD_9R5fHt5Nf7hH8m28-UDoZJkY8iUBJLI73kd-u2BE,5794
|
406
|
+
unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py,sha256=v0yP5MRGYJAb9bp2yXnp5yUmYKJ6aAKjHcNHigL_ONY,13981
|
395
407
|
unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py,sha256=sQpvIJa5-Iv03KZfC2sP2zB8XSPCZAjLpUMpNBOA-xM,3897
|
396
|
-
unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py,sha256=
|
397
|
-
unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py,sha256=
|
408
|
+
unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py,sha256=KqCLsXB_9rV4hNdSPrNynK3G-UIsipqsZT6X0Z-iM5E,13175
|
409
|
+
unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py,sha256=aMtEOpCkxH-v2BBOYj4xABzPKcDYh_jieGfaIp4hy9w,5727
|
398
410
|
unit_tests/sources/streams/concurrent/scenarios/utils.py,sha256=Pl1F4asW8AvV6bV5W3Qg21GiLqfdMT_rOt1CsFA0aVM,1953
|
399
411
|
unit_tests/sources/streams/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
400
412
|
unit_tests/sources/streams/http/test_availability_strategy.py,sha256=kuQJ5FIc4lffpHmEUVzvoN1QXQzvz8WEkFvzHItiipg,6063
|
@@ -411,8 +423,8 @@ unit_tests/utils/test_schema_inferrer.py,sha256=Z2jHBZ540wnYkylIdV_2xr75Vtwlxuyg
|
|
411
423
|
unit_tests/utils/test_secret_utils.py,sha256=XKe0f1RHYii8iwE6ATmBr5JGDI1pzzrnZUGdUSMJQP4,4886
|
412
424
|
unit_tests/utils/test_stream_status_utils.py,sha256=Xr8MZ2HWgTVIyMbywDvuYkRaUF4RZLQOT8-JjvcfR24,2970
|
413
425
|
unit_tests/utils/test_traced_exception.py,sha256=bDFP5zMBizFenz6V2WvEZTRCKGB5ijh3DBezjbfoYIs,4198
|
414
|
-
airbyte_cdk-0.
|
415
|
-
airbyte_cdk-0.
|
416
|
-
airbyte_cdk-0.
|
417
|
-
airbyte_cdk-0.
|
418
|
-
airbyte_cdk-0.
|
426
|
+
airbyte_cdk-0.55.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
427
|
+
airbyte_cdk-0.55.0.dist-info/METADATA,sha256=2r6pncfFhwFGRcafz2bnvxd-3BwRcgkvHktsJhG0Z8Q,11983
|
428
|
+
airbyte_cdk-0.55.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
429
|
+
airbyte_cdk-0.55.0.dist-info/top_level.txt,sha256=edvsDKTnE6sD2wfCUaeTfKf5gQIL6CPVMwVL2sWZzqo,51
|
430
|
+
airbyte_cdk-0.55.0.dist-info/RECORD,,
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
import logging
|
5
|
+
from typing import Any, List, Mapping, Optional, Tuple
|
6
|
+
from unittest.mock import Mock
|
7
|
+
|
8
|
+
import freezegun
|
9
|
+
from airbyte_cdk.models import (
|
10
|
+
AirbyteMessage,
|
11
|
+
AirbyteRecordMessage,
|
12
|
+
AirbyteStream,
|
13
|
+
ConfiguredAirbyteCatalog,
|
14
|
+
ConfiguredAirbyteStream,
|
15
|
+
DestinationSyncMode,
|
16
|
+
SyncMode,
|
17
|
+
)
|
18
|
+
from airbyte_cdk.models import Type as MessageType
|
19
|
+
from airbyte_cdk.sources.concurrent_source.concurrent_source_adapter import ConcurrentSourceAdapter
|
20
|
+
from airbyte_cdk.sources.message import InMemoryMessageRepository
|
21
|
+
from airbyte_cdk.sources.streams import Stream
|
22
|
+
from airbyte_cdk.sources.streams.concurrent.adapters import StreamFacade
|
23
|
+
from airbyte_cdk.sources.streams.concurrent.cursor import NoopCursor
|
24
|
+
|
25
|
+
|
26
|
+
class _MockSource(ConcurrentSourceAdapter):
|
27
|
+
def __init__(self, concurrent_source, _streams_to_is_concurrent, logger):
|
28
|
+
super().__init__(concurrent_source)
|
29
|
+
self._streams_to_is_concurrent = _streams_to_is_concurrent
|
30
|
+
self._logger = logger
|
31
|
+
|
32
|
+
message_repository = InMemoryMessageRepository()
|
33
|
+
|
34
|
+
def check_connection(self, logger: logging.Logger, config: Mapping[str, Any]) -> Tuple[bool, Optional[Any]]:
|
35
|
+
raise NotImplementedError
|
36
|
+
|
37
|
+
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
|
38
|
+
return [
|
39
|
+
StreamFacade.create_from_stream(s, self, self._logger, None, NoopCursor()) if is_concurrent else s
|
40
|
+
for s, is_concurrent in self._streams_to_is_concurrent.items()
|
41
|
+
]
|
42
|
+
|
43
|
+
|
44
|
+
@freezegun.freeze_time("2020-01-01T00:00:00")
|
45
|
+
def test_concurrent_source_adapter():
|
46
|
+
concurrent_source = Mock()
|
47
|
+
message_from_concurrent_stream = AirbyteMessage(
|
48
|
+
type=MessageType.RECORD,
|
49
|
+
record=AirbyteRecordMessage(
|
50
|
+
stream="s2",
|
51
|
+
data={"data": 2},
|
52
|
+
emitted_at=1577836800000,
|
53
|
+
),
|
54
|
+
)
|
55
|
+
concurrent_source.read.return_value = iter([message_from_concurrent_stream])
|
56
|
+
regular_stream = _mock_stream("s1", [{"data": 1}])
|
57
|
+
concurrent_stream = _mock_stream("s2", [])
|
58
|
+
unavailable_stream = _mock_stream("s3", [{"data": 3}], False)
|
59
|
+
concurrent_stream.name = "s2"
|
60
|
+
logger = Mock()
|
61
|
+
adapter = _MockSource(concurrent_source, {regular_stream: False, concurrent_stream: True, unavailable_stream: False}, logger)
|
62
|
+
|
63
|
+
messages = list(adapter.read(logger, {}, _configured_catalog([regular_stream, concurrent_stream, unavailable_stream])))
|
64
|
+
records = [m for m in messages if m.type == MessageType.RECORD]
|
65
|
+
|
66
|
+
expected_records = [
|
67
|
+
message_from_concurrent_stream,
|
68
|
+
AirbyteMessage(
|
69
|
+
type=MessageType.RECORD,
|
70
|
+
record=AirbyteRecordMessage(
|
71
|
+
stream="s1",
|
72
|
+
data={"data": 1},
|
73
|
+
emitted_at=1577836800000,
|
74
|
+
),
|
75
|
+
),
|
76
|
+
]
|
77
|
+
|
78
|
+
assert records == expected_records
|
79
|
+
|
80
|
+
|
81
|
+
def _mock_stream(name: str, data=[], available: bool = True):
|
82
|
+
s = Mock()
|
83
|
+
s.name = name
|
84
|
+
s.as_airbyte_stream.return_value = AirbyteStream(
|
85
|
+
name=name,
|
86
|
+
json_schema={},
|
87
|
+
supported_sync_modes=[SyncMode.full_refresh],
|
88
|
+
)
|
89
|
+
s.check_availability.return_value = (True, None) if available else (False, "not available")
|
90
|
+
s.read_full_refresh.return_value = iter(data)
|
91
|
+
s.primary_key = None
|
92
|
+
return s
|
93
|
+
|
94
|
+
|
95
|
+
def _configured_catalog(streams: List[Stream]):
|
96
|
+
return ConfiguredAirbyteCatalog(
|
97
|
+
streams=[
|
98
|
+
ConfiguredAirbyteStream(
|
99
|
+
stream=stream.as_airbyte_stream(),
|
100
|
+
sync_mode=SyncMode.full_refresh,
|
101
|
+
destination_sync_mode=DestinationSyncMode.overwrite,
|
102
|
+
)
|
103
|
+
for stream in streams
|
104
|
+
]
|
105
|
+
)
|
@@ -1,11 +1,14 @@
|
|
1
1
|
#
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
|
+
import concurrent
|
4
5
|
import logging
|
5
6
|
from typing import Any, List, Mapping, Optional, Tuple, Union
|
6
7
|
|
7
8
|
from airbyte_cdk.models import AirbyteStateMessage, ConfiguredAirbyteCatalog, ConnectorSpecification, DestinationSyncMode, SyncMode
|
8
|
-
from airbyte_cdk.sources import
|
9
|
+
from airbyte_cdk.sources.concurrent_source.concurrent_source import ConcurrentSource
|
10
|
+
from airbyte_cdk.sources.concurrent_source.concurrent_source_adapter import ConcurrentSourceAdapter
|
11
|
+
from airbyte_cdk.sources.concurrent_source.thread_pool_manager import ThreadPoolManager
|
9
12
|
from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
|
10
13
|
from airbyte_cdk.sources.message import InMemoryMessageRepository, MessageRepository
|
11
14
|
from airbyte_cdk.sources.streams import Stream
|
@@ -14,6 +17,7 @@ from airbyte_cdk.sources.streams.concurrent.cursor import ConcurrentCursor, Curs
|
|
14
17
|
from airbyte_cdk.sources.streams.concurrent.state_converter import EpochValueConcurrentStreamStateConverter
|
15
18
|
from airbyte_protocol.models import ConfiguredAirbyteStream
|
16
19
|
from unit_tests.sources.file_based.scenarios.scenario_builder import SourceBuilder
|
20
|
+
from unit_tests.sources.streams.concurrent.scenarios.thread_based_concurrent_stream_source_builder import NeverLogSliceLogger
|
17
21
|
|
18
22
|
_NO_STATE = None
|
19
23
|
|
@@ -22,18 +26,21 @@ class StreamFacadeConcurrentConnectorStateConverter(EpochValueConcurrentStreamSt
|
|
22
26
|
pass
|
23
27
|
|
24
28
|
|
25
|
-
class StreamFacadeSource(
|
29
|
+
class StreamFacadeSource(ConcurrentSourceAdapter):
|
26
30
|
def __init__(
|
27
31
|
self,
|
28
32
|
streams: List[Stream],
|
29
|
-
|
33
|
+
threadpool: concurrent.futures.ThreadPoolExecutor,
|
30
34
|
cursor_field: Optional[CursorField] = None,
|
31
35
|
cursor_boundaries: Optional[Tuple[str, str]] = None,
|
32
36
|
input_state: Optional[List[Mapping[str, Any]]] = _NO_STATE,
|
33
37
|
):
|
34
|
-
self._streams = streams
|
35
|
-
self._max_workers = max_workers
|
36
38
|
self._message_repository = InMemoryMessageRepository()
|
39
|
+
threadpool_manager = ThreadPoolManager(threadpool, streams[0].logger)
|
40
|
+
concurrent_source = ConcurrentSource(threadpool_manager, streams[0].logger, NeverLogSliceLogger(), self._message_repository)
|
41
|
+
super().__init__(concurrent_source)
|
42
|
+
self._streams = streams
|
43
|
+
self._threadpool = threadpool_manager
|
37
44
|
self._cursor_field = cursor_field
|
38
45
|
self._cursor_boundaries = cursor_boundaries
|
39
46
|
self._state = [AirbyteStateMessage.parse_obj(s) for s in input_state] if input_state else None
|
@@ -49,7 +56,6 @@ class StreamFacadeSource(AbstractSource):
|
|
49
56
|
stream,
|
50
57
|
self,
|
51
58
|
stream.logger,
|
52
|
-
self._max_workers,
|
53
59
|
state_converter.get_concurrent_stream_state(state_manager.get_stream_state(stream.name, stream.namespace)),
|
54
60
|
ConcurrentCursor(
|
55
61
|
stream.name,
|
@@ -115,4 +121,5 @@ class StreamFacadeSourceBuilder(SourceBuilder[StreamFacadeSource]):
|
|
115
121
|
return self
|
116
122
|
|
117
123
|
def build(self, configured_catalog: Optional[Mapping[str, Any]]) -> StreamFacadeSource:
|
118
|
-
|
124
|
+
threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=self._max_workers, thread_name_prefix="workerpool")
|
125
|
+
return StreamFacadeSource(self._streams, threadpool, self._cursor_field, self._cursor_boundaries, self._input_state)
|