Producer¶
What is Producer¶
The Producer is a program that continuously generates records and sends them to a target system. To optimize performance, it utilize a buffer to send data in batches. Regardless of whether it’s the producer program or the buffer program that encounters a failure, we must have the capability to restart the program and retry the operation without data loss. This retry process should be executed gracefully, incorporating an exponential backoff strategy. Upon successful completion, we can remove the data from the buffer to save space. This action is referred to as “commit”.
Error Handling¶
What exponential backoff mean is that, we wait longer and longer between each retry and stop retrying at certain number of failure. The wait internal and max retry count strategy is called a “Schedule”.
When the max retry count is reached, we have two options:
skip: persist the context data of this batch for debug or future retry, and skip to the next batch.
raise: raise error immediately and stop the program.
SimpleProducer Example¶
Below is the sample usage of SimpleProducer, a simple producer that send data to a target file on your local machine in append-only mode. This producer is for demo and for testing purpose.
simple_producer.py
1# -*- coding: utf-8 -*-
2
3import typing as T
4import time
5import random
6import shutil
7import dataclasses
8from pathlib import Path
9
10from unistream.api import (
11 SendError,
12 DataClassRecord,
13 FileBuffer,
14 RetryConfig,
15 SimpleProducer,
16)
17
18
19def rand_value() -> int:
20 return random.randint(1, 100)
21
22
23@dataclasses.dataclass(frozen=True)
24class MyRecord(DataClassRecord):
25 value: int = dataclasses.field(default_factory=rand_value)
26
27
28@dataclasses.dataclass
29class MyProducer(SimpleProducer):
30 def send(self, records: T.List[MyRecord]):
31 if random.randint(1, 100) <= 50:
32 raise SendError("randomly failed due to send error")
33 super().send(records)
34
35
36dir_demo = Path(__file__).absolute().parent.joinpath("simple_producer_demo")
37shutil.rmtree(dir_demo, ignore_errors=True)
38dir_demo.mkdir(exist_ok=True)
39
40path_log = dir_demo / "simple_producer_buffer.log"
41path_client_target = dir_demo / "simple_producer_history.log"
42
43
44def make_producer() -> MyProducer:
45 producer = MyProducer.new(
46 buffer=FileBuffer.new(
47 record_class=MyRecord,
48 path_wal=path_log,
49 max_records=3,
50 ),
51 retry_config=RetryConfig(
52 exp_backoff=[1, 2, 4],
53 ),
54 path_sink=path_client_target,
55 )
56 return producer
57
58
59producer = make_producer()
60
61# --- test 1 ---
62n = 15
63for i in range(1, 1 + n):
64 time.sleep(1)
65 # The producer program can be terminated with a 30% chance.
66 # we create a new producer object to simulate that.
67 if random.randint(1, 100) <= 30:
68 producer = make_producer()
69 producer.put(DataClassRecord(id=str(i)), verbose=True)
70
71# --- test 2 ---
72# producer.retry_config.exp_backoff = [0.1, 0.2, 0.4]
73# n = 1000
74# for i in range(1, 1 + n):
75# time.sleep(0.001)
76# producer.put(DataClassRecord(id=str(i)), verbose=True)
simple_producer.py Output
+----- ⏱ 📤 Start 'put record' -------------------------------------------------+
📤
📤 record = {"id": "1", "create_at": "2024-01-07T07:31:41.482432+00:00"}
📤 🚫 we should not emit
📤
+----- ⏰ ✅ 📤 End 'put record', elapsed = 0.00 sec -----------------------------+
+----- ⏱ 📤 Start 'put record' -------------------------------------------------+
📤
📤 record = {"id": "2", "create_at": "2024-01-07T07:31:42.486702+00:00"}
📤 🚫 we should not emit
📤
+----- ⏰ ✅ 📤 End 'put record', elapsed = 0.00 sec -----------------------------+
+----- ⏱ 📤 Start 'put record' -------------------------------------------------+
📤
📤 record = {"id": "3", "create_at": "2024-01-07T07:31:43.487467+00:00"}
📤 📤 send records: ['1', '2', '3']
📤 🔴 failed, error: SendError('randomly failed due to send error')
📤
+----- ⏰ ✅ 📤 End 'put record', elapsed = 0.00 sec -----------------------------+
+----- ⏱ 📤 Start 'put record' -------------------------------------------------+
📤
📤 record = {"id": "4", "create_at": "2024-01-07T07:31:44.493604+00:00"}
📤 📤 send records: ['1', '2', '3']
📤 🟢 succeeded
📤
+----- ⏰ ✅ 📤 End 'put record', elapsed = 0.00 sec -----------------------------+
Vendor-Specific Producers¶
Note
Vendor-specific producers (AWS CloudWatch Logs, AWS Kinesis Stream, Apache Kafka, etc.) are released as separate plugin packages — for example unistream-aws-cloudwatch, unistream-aws-kinesis, unistream-kafka. Refer to those plugins for usage examples and installation instructions.