Skip to content

KAFKA-20173: Ensure Metered kv-stores pass headers correctly#21768

Open
mjsax wants to merge 1 commit intoapache:trunkfrom
mjsax:kafka-20173-passing-headers-metered-stores
Open

KAFKA-20173: Ensure Metered kv-stores pass headers correctly#21768
mjsax wants to merge 1 commit intoapache:trunkfrom
mjsax:kafka-20173-passing-headers-metered-stores

Conversation

@mjsax
Copy link
Member

@mjsax mjsax commented Mar 16, 2026

Ensures that all Metered KV-stores (plain, ts, headers, version) pass
headers into de/serializers.

Ensures that all Metered KV-stores (plain, ts, headers, version)
pass headers into de/serializers.
@mjsax mjsax added streams kip Requires or implements a KIP labels Mar 16, 2026
protected Sensor getSensor;
protected Sensor deleteSensor;
private Sensor putAllSensor;
protected Sensor putAllSensor;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need access in MeteredKeyValueTimestampStoreWithHeaders

if (rawResult.isSuccess()) {
final KeyValueIterator<Bytes, byte[]> iterator = rawResult.getResult();
final KeyValueIterator<K, V> resultIterator = new MeteredKeyValueTimestampedIterator(
final KeyValueIterator<K, V> resultIterator = new MeteredKeyValueStoreIterator(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side cleanup: I think there is no reason to have MeteredKeyValueTimestampedIterator (removing blow), and we can just use existing MeteredKeyValueStoreIterator. -- We don't have the case to bridge between different value types in MeteredKeyValueStore.

Objects.requireNonNull(prefix, "prefix cannot be null");
Objects.requireNonNull(prefixKeySerializer, "prefixKeySerializer cannot be null");
return new MeteredKeyValueIterator(wrapped().prefixScan(prefix, prefixKeySerializer), prefixScanSensor);
return new MeteredKeyValueStoreIterator(wrapped().prefixScan(prefix, prefixKeySerializer), prefixScanSensor);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just renaming, to align to established naming patterns across the different kv-metered stores.

public ValueTimestampHeaders<V> get(final K key) {
Objects.requireNonNull(key, "key cannot be null");
try {
return maybeMeasureLatency(() -> deserializeValue(wrapped().get(serializeKey(key, new RecordHeaders()))), time, getSensor);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to overwrite get() to be able to pass in headers into serializeKey(...) -- For get() it might not be strictly necessary, but if forces us to make a conscious decision what to pass -- it's a side-effect of disallowing to call serializeKey w/o headers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we would want to pass context.headers() instead of new RecordHeaders(), similar to what we did in #21684

If yes, applies elsewhere in this class, too.

try {
final Headers headers = value != null ? value.headers() : new RecordHeaders();
maybeMeasureLatency(() -> wrapped().put(keyBytes(key, headers), serdes.rawValue(value, headers)), time, putSensor);
maybeMeasureLatency(() -> wrapped().put(serializeKey(key, headers), serdes.rawValue(value, headers)), time, putSensor);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unifying the existing keyBytes method with the naming of MeteredKeyValueStore which uses serializeKey

if (rawResult.isSuccess()) {
final KeyValueIterator<Bytes, byte[]> iterator = rawResult.getResult();
final KeyValueIterator<K, V> resultIterator = new MeteredTimestampedKeyValueStoreWithHeadersIterator(
final KeyValueIterator<K, V> resultIterator = new MeteredTimestampedKeyValueStoreWithHeadersQueryIterator(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just renaming to aling naming patterns.

Objects.requireNonNull(prefix, "prefix cannot be null");
Objects.requireNonNull(prefixKeySerializer, "prefixKeySerializer cannot be null");
return new MeteredValueTimestampHeadersIterator(
return new MeteredTimestampedKeyValueStoreWithHeadersIterator(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just renaming to align naming patterns


final KeyValue<Bytes, byte[]> keyValue = iter.next();
final ValueTimestampHeaders<V> valueTimestampHeaders = valueTimestampHeadersDeserializer.apply(keyValue.value);
final Headers headers = valueTimestampHeaders != null ? valueTimestampHeaders.headers() : new RecordHeaders();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can never be null. We know that iter.next cannot return null (this would be a "non existing row" -- null-values are deletes).

Thus simplifying the code (we don't have any null checks for this case in older code for plain- and ts-store either.

Similar below

protected Bytes keyBytes(final K key, final Headers headers) {
@Override
protected Bytes serializeKey(final K key) {
throw new UnsupportedOperationException("MeteredTimestampedKeyValueStoreWithHeaders required to pass in Headers when serializing a key.");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually surfacing the missing overrides, highlighting the bug to not override delete. So a good guard to have in place (little bit annoying side effect that it forces us to override put/putAll, too, but I think it's worth the prices to pay)


@Override
protected K deserializeKey(final byte[] rawKey) {
throw new UnsupportedOperationException("MeteredTimestampedKeyValueStoreWithHeaders required to pass in Headers when deserializing a key.");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar guard -- this did not surface any issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kip Requires or implements a KIP streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant