abstraction

Note

Maintainer notes

This module declares some important concepts and their interfaces.

class unistream.abstraction.AbcRecord[source]

Abstract Class for a Record to Be Sent to a Target System

In the context of this library, a “record” refers to a structured data container. This abstract class provides a foundation that you can extend to implement your own data model specific to your project. The Python community offers several excellent libraries for data modeling, including:

This abstract class should include the following attributes:

  • id: unique identifier for the record.

  • create_at: the ISO8601 representation of the creation time of the record.

    it has to be timezone aware.

Additionally, the class should provide the following methods:

property create_at_datetime: datetime

Return the datetime object of the creation time of the record.

abstract serialize() str[source]

Serialize the record to a string.

abstract classmethod deserialize(data: str)[source]

Deserialize the string to a record.

class unistream.abstraction.AbcBuffer[source]

Abstract Buffer Class for Data Producers

The abstract buffer class is designed to be used in data producer applications. Buffers play a crucial role in temporarily storing records before sending them to the target system. This allows us to optimize the utilization of network bandwidth efficiently.

Buffer Functionality

Buffers naturally follow a FIFO (First-In-First-Out) queue structure.

Fault-Tolerant Behavior

One of the key features of a buffer is its fault tolerance. It should be capable of recovering from a crash or system failure. For instance, when a record is placed in the buffer, it should be immediately persisted to ensure data durability.

Buffer Capacity

A buffer has to have these two attributes:

Parameters:
  • max_records – The maximum number of records that can be stored within the buffer.

  • max_size – The maximum total size, in bytes, for records that can be stored in the buffer.

When the in-memory queue reaches its maximum capacity (either in terms of records or size), the buffer will automatically write the in-memory data to persistent storage and clear the in-memory queue.

Buffer Operations

Users can interact with the buffer in the following ways:

In summary, this abstract buffer class provides a flexible and fault-tolerant mechanism for managing data records in a data producer application.

abstract classmethod new(**kwargs)[source]

Factory method to create a buffer. It should try to recovery unsent records from persistence layer.

abstract put(record: AbcRecord)[source]

Put a record into the in-memory queue.

abstract should_i_emit() bool[source]

Identify whether the buffer should emit records.

abstract emit() list[AbcRecord][source]

Emit a list of records. Older records comes first.

abstract commit()[source]

Mark the previously emitted records as no-longer-need. Typically, it removes the records from the persistence layer.

class unistream.abstraction.AbcProducer[source]

Abstract Class for Data Producers

A data producer is an application responsible for generating data records and seamlessly sending them to a target system. It simplifies the process for users, allowing them to focus on creating data records without needing to concern themselves with low-level details such as API calls, retry mechanisms, or fault tolerance.

A producer has to have a buffer attribute, which is an instance of a subclass of AbcBuffer.

Producer Operations

abstract classmethod new(**kwargs)[source]

Factory method to create a producer.

abstract send(records: Iterable[AbcRecord])[source]

Send batch records to target system.

Note

You don’t need to include any logic for error handling, retry, buffer. Just think of how to send a batch of records. Those logics will be handled by the buffer and other methods.

In your producer application code, you only need to call this method explicitly, you only need to call AbsProducer.put() method and this method will be called when buffer is full.

abstract put(record: AbcRecord, raise_send_error: bool = False, verbose: bool = False)[source]

Put the record to the buffer and smartly decide whether to send the records.

class unistream.abstraction.AbcCheckPoint[source]

Abstract Checkpoint Class for Data Consumer

The CheckPoint class serves as a crucial component for data consumers. It stores essential information, including processing status, processing metadata, and the original record data. Its primary purpose is to ensure data integrity and achieve exactly-once processing.

CheckPoint Functionality

  1. Management of Stream Pointers: Many stream systems feature a concept

    known as a “pointer” that indicates where to begin pulling data. In Kafka, this pointer is called an offset, while in Kinesis, it is referred to as a shard iterator. CheckPoint stores these pointers in the persistence layer, allowing consumers to resume from the last checkpoint in the event of a restart.

  2. Batch Data Backup: When a consumer receives a batch of records,

    CheckPoint creates a short-lived backup of these records. This backup ensures that, even in scenarios where both the records and pointers are lost, the batch data can still be recovered from the checkpoint.

  3. Handling Record Processing: Consumers may choose to consume batch records

    sequentially or in parallel. Before processing a record, CheckPoint sets its status as “in-progress” and locks the record to prevent other consumers from processing the same record concurrently. After processing, the checkpoint updates the record status to one of the following: “failed,” “exhausted” (retried too many times), or “succeeded,” and subsequently unlocks the record. In the event of a consumer crash during record processing, the record will be automatically unlocked after a timeout period.

CheckPoint Operations

dump()[source]

Dump the checkpoint data to the persistence layer.

classmethod load(**kwargs)[source]

Load the checkpoint data from the persistence layer.

It has to handle the edge case that the checkpoint data does not exist.

dump_records(records: Iterable[AbcRecord])[source]

Dump the batch records data to the persistence layer.

load_records(record_class: type[AbcRecord], **kwargs) Iterable[AbcRecord][source]

Load the batch records data from the persistence layer. Not from the stream system.

mark_as_in_progress(record: AbcRecord, **kwargs)[source]

Set status as in_progress and lock the record so other workers can’t process it.

Note

This method only updates the in-memory data. It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

mark_as_failed_or_exhausted(record: AbcRecord, **kwargs)[source]

Mark the tracker as failed or exhausted and release the lock.

Note

This method only updates the in-memory data. It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

mark_as_succeeded(record: AbcRecord, **kwargs)[source]

Mark the tracker as succeeded and release the lock.

Note

This method only updates the in-memory data. It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

dump_as_in_progress(record: AbcRecord)[source]

Dump the tracker to the persistence layer after calling BaseCheckpoint.mark_as_in_progress.

Note

It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

dump_as_failed_or_exhausted(record: AbcRecord)[source]

Dump the tracker to the persistence layer after calling BaseCheckpoint.mark_as_failed_or_exhausted.

Note

It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

dump_as_succeeded(record: AbcRecord)[source]

Dump the tracker to the persistence layer after calling BaseCheckpoint.mark_as_in_progress.

Note

It is up to the developer to implement the persistence layer to persist the data.

Parameters:

record – the record we are tracking.

class unistream.abstraction.AbcConsumer[source]

Abstract Class for Data Consumer

A consumer is an application that continuously retrieves data from a stream system and processes it, either sequentially or in parallel. This abstract class simplifies the development work required for creating a consumer application. Users only need to focus on implementing how they want to process the data and how to handle failed data. This class automatically manages crucial aspects such as checkpointing, retries, and more.

A consumer must have a checkpoint attribute, which should be an instance of a subclass of AbcCheckpoint.

Consumer Operations

abstract classmethod new(**kwargs)[source]

Factory method to create a consumer.

abstract get_records() Iterable[AbcRecord][source]

Get records from the target system.

abstract process_record(record: AbcRecord)[source]

Process a record. To indicate the processing is failed, it has to raise an exception.

abstract process_failed_record(record: AbcRecord)[source]

Process a failed record.