Connector types
The SDK ships four connector base classes. They differ in how data flows through them, not in what they produce.
The whole dataset fits comfortably in memory. Wikis, service catalogs, config databases.
BaseDataClientDocumentDefinitionThe dataset is large or paginated and your source client is synchronous.
BaseStreamingDataClientDocumentDefinitionSame as streaming, but your source client is async — httpx.AsyncClient, aiohttp.
BaseAsyncStreamingDataClientDocumentDefinitionYou're indexing employee and identity records rather than documents.
BaseDataClientEmployeeInfoDefinitionIf you're unsure, start with BaseDatasourceConnector. Switching to a
streaming variant later means changing the base class and the return type of
get_source_data() — transform() is unchanged.
In memory
get_source_data() returns a Sequence. The connector fetches everything,
transforms everything, then uploads in batches of batch_size (default 1000).
class WikiDataClient(BaseDataClient[WikiPage]):
def get_source_data(self, since=None, **kwargs):
return fetch_all_pages()
class WikiConnector(BaseDatasourceConnector[WikiPage]):
configuration = CustomDatasourceConfig(name="wiki", display_name="Wiki")
def transform(self, data):
return [to_document(page) for page in data]
Peak memory holds every source record and every transformed document at once. At a few hundred thousand documents that becomes the binding constraint — and the fix is a streaming connector, not a smaller batch size, because batching happens after transform.
Sync streaming
get_source_data() is a generator. The connector pulls batch_size items at a
time, transforms that slice, and uploads it before pulling more.
from collections.abc import Generator
from glean.indexing.connectors import (
BaseStreamingDataClient,
BaseStreamingDatasourceConnector,
)
class ArticleDataClient(BaseStreamingDataClient[Article]):
def get_source_data(self, **kwargs) -> Generator[Article, None, None]:
page = 1
while True:
batch = fetch_page(page)
if not batch:
return
yield from batch
page += 1
class ArticleConnector(BaseStreamingDatasourceConnector[Article]):
configuration = CustomDatasourceConfig(name="articles", display_name="Articles")
def transform(self, data):
return [to_document(article) for article in data]
Note that transform() still receives a Sequence — the SDK slices the
generator into batches for you. You never write batching logic.
If your source is a paginated HTTP API, use
BasePullHttpStreamingDataClient
instead of hand-rolling the loop above. It handles pagination, retries, and
rate limiting.
Async streaming
Same model, async generators. Drive it with index_data_async().
from collections.abc import AsyncGenerator
from glean.indexing.connectors import (
BaseAsyncStreamingDataClient,
BaseAsyncStreamingDatasourceConnector,
)
class EventDataClient(BaseAsyncStreamingDataClient[Event]):
async def get_source_data(self, **kwargs) -> AsyncGenerator[Event, None]:
async for event in stream_events():
yield event
class EventConnector(BaseAsyncStreamingDatasourceConnector[Event]):
configuration = CustomDatasourceConfig(name="events", display_name="Events")
def transform(self, data):
return [to_document(event) for event in data]
await connector.index_data_async(mode=IndexingMode.FULL)
transform() stays synchronous. Only fetching is async.
Async connectors also expose the synchronous index_data(), which wraps the
async path in asyncio.run(). That works from a plain script but fails inside
an existing event loop. In tests, use await run_connector_async(...);
run_connector() raises a clear error if you call it on an async connector
from inside a running loop.
People
BasePeopleConnector produces EmployeeInfoDefinition instead of
DocumentDefinition. Configuration, batch_size, and observability behave the
same.
class EmployeeConnector(BasePeopleConnector[EmployeeRecord]):
configuration = CustomDatasourceConfig(name="hris", display_name="HRIS")
def transform(self, data):
return [to_employee(record) for record in data]
Employee data feeds Glean's people directory and knowledge graph. It's distinct from the datasource identities (users, groups, memberships) that back document ACLs — see Permissions.
Batch size
All four classes expose batch_size, defaulting to 1000.
connector = WikiConnector(name="wiki", data_client=client)
connector.batch_size = 250
Lower it if your documents are large enough to hit upload timeouts. For timeout and concurrency tuning, see Batching and throughput.