@@ -174,6 +174,95 @@ TEST(KafkaAutoCommitConsumer, PollWithHeaders)
174174 consumer.close ();
175175}
176176
177+ TEST (KafkaAutoCommitConsumer, RecordWithEmptyOrNullFields)
178+ {
179+ auto sendMessages = [](const Kafka::ProducerRecord& record, std::size_t repeat, const std::string& partitioner) {
180+ Kafka::KafkaSyncProducer producer (KafkaTestUtility::GetKafkaClientCommonConfig ()
181+ .put (ProducerConfig::PARTITIONER, partitioner));
182+ producer.setLogLevel (LOG_CRIT);
183+ for (std::size_t i = 0 ; i < repeat; ++i) {
184+ producer.send (record);
185+ }
186+ };
187+
188+ std::cout << " [" << Utility::getCurrentTime () << " ] Try with messages with empty fields" << std::endl;
189+ {
190+ const Topic topic = Utility::getRandomString ();
191+ KafkaTestUtility::CreateKafkaTopic (topic, 5 , 3 );
192+
193+ const std::string emptyStr{};
194+ const auto emptyField = ConstBuffer (emptyStr.c_str (), emptyStr.size ());
195+
196+ auto recordWithEmptyFields = Kafka::ProducerRecord (topic, emptyField, emptyField);
197+
198+ // murmur2_random, -- NULL (NOTE: not empty) keys are randomly partitioned.
199+ // This is the default for `modern-cpp-kafka` API (also is the default partitioner in the Java Producer)
200+ sendMessages (recordWithEmptyFields, 10 , " murmur2_random" );
201+
202+ // The auto-commit consumer
203+ KafkaAutoCommitConsumer consumer (KafkaTestUtility::GetKafkaClientCommonConfig ()
204+ .put (ConsumerConfig::AUTO_OFFSET_RESET, " earliest" ));
205+ // Subscribe topics
206+ consumer.subscribe ({topic});
207+
208+ // Poll all messages
209+ auto records = KafkaTestUtility::ConsumeMessagesUntilTimeout (consumer, std::chrono::seconds (1 ));
210+
211+ // Check the key/value (should be empty: 0 length, but not NULL)
212+ std::map<int , int > counts;
213+ for (const auto & record: records)
214+ {
215+ std::cout << record.toString () << std::endl;
216+
217+ EXPECT_TRUE (record.key ().size () == 0 );
218+ EXPECT_TRUE (record.value ().size () == 0 );
219+ EXPECT_TRUE (record.key ().data () != nullptr );
220+ EXPECT_TRUE (record.value ().data () != nullptr );
221+
222+ counts[record.partition ()] += 1 ;
223+ }
224+ // Should be hashed to the same partition (with empty key)
225+ EXPECT_TRUE (counts.size () == 1 );
226+ }
227+
228+ std::cout << " [" << Utility::getCurrentTime () << " ] Try with messages with NULL fields" << std::endl;
229+ {
230+ const Topic topic = Utility::getRandomString ();
231+ KafkaTestUtility::CreateKafkaTopic (topic, 5 , 3 );
232+
233+ auto recordWithNullFields = Kafka::ProducerRecord (topic, Kafka::NullKey, Kafka::NullValue);
234+
235+ // murmur2_random, -- NULL (NOTE: not empty) keys are randomly partitioned.
236+ // This is the default for `modern-cpp-kafka` API (also is the default partitioner in the Java Producer)
237+ sendMessages (recordWithNullFields, 10 , " murmur2_random" );
238+
239+ // The auto-commit consumer
240+ KafkaAutoCommitConsumer consumer (KafkaTestUtility::GetKafkaClientCommonConfig ()
241+ .put (ConsumerConfig::AUTO_OFFSET_RESET, " earliest" ));
242+ // Subscribe topics
243+ consumer.subscribe ({topic});
244+
245+ // Poll all messages
246+ auto records = KafkaTestUtility::ConsumeMessagesUntilTimeout (consumer, std::chrono::seconds (1 ));
247+
248+ // Check the key/value (should be NULL)
249+ std::map<int , int > counts;
250+ for (const auto & record: records)
251+ {
252+ std::cout << record.toString () << std::endl;
253+
254+ EXPECT_TRUE (record.key ().size () == 0 );
255+ EXPECT_TRUE (record.value ().size () == 0 );
256+ EXPECT_TRUE (record.key ().data () == nullptr );
257+ EXPECT_TRUE (record.value ().data () == nullptr );
258+
259+ counts[record.partition ()] += 1 ;
260+ }
261+ // Should be hashed to random partitions (with NULL key)
262+ EXPECT_TRUE (counts.size () > 1 );
263+ }
264+ }
265+
177266TEST (KafkaAutoCommitConsumer, SeekAndPoll)
178267{
179268 const Topic topic = Utility::getRandomString ();
0 commit comments