|
4 | 4 |
|
5 | 5 | namespace SimPod\Kafka\Clients\Producer; |
6 | 6 |
|
| 7 | +use InvalidArgumentException; |
7 | 8 | use RdKafka\Producer; |
| 9 | +use RdKafka\ProducerTopic; |
| 10 | +use RuntimeException; |
| 11 | +use function sprintf; |
8 | 12 | use const RD_KAFKA_PARTITION_UA; |
| 13 | +use const RD_KAFKA_RESP_ERR_NO_ERROR; |
9 | 14 |
|
10 | 15 | class KafkaProducer extends Producer |
11 | 16 | { |
12 | 17 | private const RD_KAFKA_MSG_F_COPY = 0; |
13 | 18 |
|
14 | | - public function __construct(ProducerConfig $config) |
| 19 | + /** @var callable(KafkaProducer):void|null */ |
| 20 | + private $exitCallback; |
| 21 | + |
| 22 | + /** @param callable(KafkaProducer):void|null $exitCallback */ |
| 23 | + public function __construct(ProducerConfig $config, ?callable $exitCallback = null) |
15 | 24 | { |
| 25 | + $this->exitCallback = $exitCallback; |
| 26 | + |
16 | 27 | parent::__construct($config->getConf()); |
17 | 28 | } |
18 | 29 |
|
19 | | - public function produce(ProducerRecord $record) : void |
| 30 | + public function __destruct() |
20 | 31 | { |
21 | | - $topic = $this->newTopic($record->topic); |
22 | | - /** @psalm-suppress UndefinedMethod https://github.com/vimeo/psalm/issues/3406 */ |
23 | | - $topic->produce($record->partition ?? RD_KAFKA_PARTITION_UA, self::RD_KAFKA_MSG_F_COPY, $record->value, $record->key); |
| 32 | + if ($this->exitCallback === null) { |
| 33 | + return; |
| 34 | + } |
| 35 | + |
| 36 | + ($this->exitCallback)($this); |
| 37 | + } |
| 38 | + |
| 39 | + /** @param array<string, string>|null $headers */ |
| 40 | + public function produce( |
| 41 | + string $topicName, |
| 42 | + ?int $partition, |
| 43 | + string $value, |
| 44 | + ?string $key = null, |
| 45 | + ?array $headers = null, |
| 46 | + ?int $timestampMs = null |
| 47 | + ) : void { |
| 48 | + if ($partition < 0) { |
| 49 | + throw new InvalidArgumentException( |
| 50 | + sprintf('Invalid partition: %d. Partition number should always be non-negative or null.', $partition) |
| 51 | + ); |
| 52 | + } |
| 53 | + |
| 54 | + /** @psalm-var ProducerTopic $topic Psalm thinks this is a Topic https://github.com/vimeo/psalm/issues/3406 */ |
| 55 | + $topic = $this->newTopic($topicName); |
| 56 | + $topic->producev( |
| 57 | + $partition ?? RD_KAFKA_PARTITION_UA, |
| 58 | + self::RD_KAFKA_MSG_F_COPY, |
| 59 | + $value, |
| 60 | + $key, |
| 61 | + $headers, |
| 62 | + $timestampMs |
| 63 | + ); |
24 | 64 | $this->poll(0); |
25 | 65 | } |
26 | 66 |
|
27 | | - public function flush() : void |
| 67 | + public function flushMessages(int $timeoutMs = 10000) : void |
28 | 68 | { |
29 | | - while ($this->getOutQLen() > 0) { |
30 | | - $this->poll(1); |
| 69 | + $result = null; |
| 70 | + for ($flushRetries = 0; $flushRetries < 10; $flushRetries++) { |
| 71 | + $result = $this->flush($timeoutMs); |
| 72 | + if ($result === RD_KAFKA_RESP_ERR_NO_ERROR) { |
| 73 | + break; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + if ($result !== RD_KAFKA_RESP_ERR_NO_ERROR) { |
| 78 | + throw new RuntimeException('Was unable to flush, messages might be lost!'); |
31 | 79 | } |
32 | 80 | } |
33 | 81 | } |
0 commit comments