Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
d472a94
Merged and resolved differences. Left a number of TODOs, and now tryi…
May 15, 2019
78ccb49
oneToMany join Working, with a number of prefixScans stubbed out. Als…
May 16, 2019
b47b3e3
Changed SubscriptionResolverJoinProcessorSupplier to use the KTableVa…
May 30, 2019
3c7ad51
"Fixed" an issue where the SubscriptionResponseWrapper was sending a…
May 31, 2019
1b07216
Added the code that should support the prefixscan in the timestamped …
May 31, 2019
74f4ae8
prefixScan provisionally working on a number of previously unsupporte…
Jun 3, 2019
40fca2d
Populated AbstractKeyValueStoreTest.java with prefixScan tests equiva…
Jun 3, 2019
cb2e625
Added more tests. Incorrectly used Bytes.MAX_VALUE, replaced it with …
Jun 3, 2019
75ea876
Split serdes and combinedkey into their own test files. Fixed some un…
Jun 4, 2019
beaa82f
Enforcing co-partitioning of source topics to be identical to that of…
Jun 4, 2019
897fa90
Empty test, did not need
Jun 4, 2019
4bde43d
Rebased to the latest trunk
Jun 4, 2019
d687dbb
Fixed a few erroring unit tests with incorrect asserts (not sure how …
Jun 5, 2019
73cc69e
Potential fix for the timestamp store casting issue.
Jun 6, 2019
9c31fcd
Add the missing licenses
Jun 7, 2019
5ad29a6
Potential solution for prefixScan bug that I had introduced previousl…
Jun 25, 2019
8a8e10d
Reworking the test cases to better exercise all of the logical nuance…
Jun 26, 2019
bb84b8a
Added leftJoin for foreignKey joiner. Modified the SubscriptionWrappe…
Jun 27, 2019
6d80434
Added commit hash and sources to Murmur3 and Murmur3Test to indicate …
Jun 27, 2019
2f11d5d
Fixed all the checkstyle and spotbug issues in streams and clients. A…
Jun 27, 2019
fa358ce
Must add exclusions to spotbugs for the copied Murmur3 file. The good…
Jun 27, 2019
9f65880
Fixed a formatting failure with scala-streams. Added some clearer com…
Jun 27, 2019
8695037
Forgot to close the iterator after a prefixScan
Jun 28, 2019
c7ccdc2
...aaaaand I was missing a closing bracket... sheesh
Jun 28, 2019
7ec664b
Added a bunch of comments and attempted to clarify the topology by ad…
Jun 28, 2019
23b4e02
Reworked the Named usage in the FKjoiners to adhere much more closely…
Jun 28, 2019
ed11b9f
Added versions and primaryKey to SubscriptionWrapper. Removed Combine…
Jun 29, 2019
947cf6d
Reworked the Serdes to indicate if the hash is null, without using mo…
Jun 29, 2019
955cf06
Added more tests and debugged SubscriptionWrapper.java and Subscripti…
Jun 29, 2019
c7d8ddf
Remove errant code that wasn't supposed to be committed
Jun 29, 2019
19b11fe
Added handling for key == null based off of logic in KStreamSessionWi…
Jun 29, 2019
aff3cd9
Copartitioning works with multiple partition count for single join, b…
Jun 29, 2019
f15ab35
Ran checkStyleMain and corrected the style errors
Jun 29, 2019
ce51bf4
Removed check to see if old and newvalue are the same in prefixscan. …
Jul 1, 2019
10eacb2
Added some comments to help with the prefixScan readability
Jul 1, 2019
dfdfc34
Not a byte
Jul 1, 2019
6add956
Do not support multiple input topic partitions. The copartitioning lo…
Jul 2, 2019
b67fae8
Changed ValueMapper to Function. Also fixed a number of SpotBugs issu…
Jul 25, 2019
fd8394c
John Roesler's extensive simplification changes
Aug 1, 2019
953da3a
Merged John Roesler's extensive simplification changes into branch.
Aug 1, 2019
2445e87
Fixed a few minor issues with imports, Murmur3 newline, and and comme…
Aug 8, 2019
a8e6b40
Merge branch 'trunk' into trunk-oneToMany
Aug 8, 2019
0dbac68
Undoing the changes made to NamedCache since it's not required.
Aug 8, 2019
1678a54
Fixed a number of issues preventing the pre-build checks and the buil…
Aug 9, 2019
543aa4f
Merge remote-tracking branch 'source/trunk' into trunk-oneToMany
Aug 28, 2019
4e58aa3
Missed a dependency on merge
Aug 28, 2019
c69824a
Cleaning up the commit by reverting a number of unneccessary files. A…
Aug 29, 2019
0536c81
Remove unused code in RocksDBTimestampedStore.java
Aug 29, 2019
259f20e
Updated the docs for KTable
Aug 29, 2019
e93c25f
Reverting some unnecessary changes
Aug 29, 2019
811b477
Reverting more seemingly unnecessary changes.
Aug 29, 2019
7c7581b
Fixed some checkstyle violations
Aug 29, 2019
2a64bf7
Trying to revert file cause I messed it up before.. bah
Aug 29, 2019
ce558e8
Revert unnecessary files
Aug 29, 2019
8b33ffb
Change Bytes.increment to throw an exception if the incremented index…
Sep 3, 2019
9da1cd9
Updated the docs on Bytes.increment()
Sep 3, 2019
67e1ee6
A few cleanups and some extra testing based on Bill's feedback
Sep 12, 2019
6917848
Add javadoc for streams
Sep 12, 2019
1e0aaae
Added the two named options to streamsResetter
Sep 13, 2019
faa3cba
Added prospective code to streamsresetter, need to find the correct p…
Sep 19, 2019
a1e7fbb
Pass in the streamsConfig to builder.build() in the integration tests
Sep 19, 2019
92ab536
Merged in trunk, moved Murmur3 to use the streams version, removed gu…
Oct 2, 2019
4c50463
Added a comment about the foreignKey leftJoin not propagating on a nu…
Oct 2, 2019
6e1b6a1
Exclude tests ending in Suite in :streams project
Oct 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,12 @@ project(':streams') {
if( !generatedDocsDir.exists() ) { generatedDocsDir.mkdirs() }
standardOutput = new File(generatedDocsDir, "streams_config.html").newOutputStream()
}

test {
// The suites are for running sets of tests in IDEs.
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
exclude '**/*Suite.class'
}
}

project(':streams:streams-scala') {
Expand Down
12 changes: 9 additions & 3 deletions checkstyle/suppressions.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>


<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
Expand Down Expand Up @@ -67,6 +67,8 @@

<suppress checks="(JavaNCSS|CyclomaticComplexity|MethodLength)"
files="CoordinatorClient.java"/>
<suppress checks="(UnnecessaryParentheses|BooleanExpressionComplexity|CyclomaticComplexity|WhitespaceAfter|LocalVariableName)"
files="Murmur3.java"/>

<suppress checks="(NPathComplexity|ClassFanOutComplexity|CyclomaticComplexity|ClassDataAbstractionCoupling|LocalVariableName|MemberName|MethodLength|JavaNCSS)"
files="clients[\\/]src[\\/](generated|generated-test)[\\/].+.java$"/>
Expand All @@ -90,6 +92,9 @@
<suppress checks="NPathComplexity"
files="MemoryRecordsTest|MetricsTest"/>

<suppress checks="(WhitespaceAround|LocalVariableName|ImportControl|AvoidStarImport)"
files="Murmur3Test.java"/>

<!-- Connect -->
<suppress checks="ClassFanOutComplexity"
files="DistributedHerder(|Test).java"/>
Expand Down Expand Up @@ -149,7 +154,7 @@
files="(TopologyBuilder|KafkaStreams|KStreamImpl|KTableImpl|StreamThread|StreamTask).java"/>

<suppress checks="MethodLength"
files="StreamsPartitionAssignor.java"/>
files="(KTableImpl|StreamsPartitionAssignor.java)"/>

<suppress checks="ParameterNumber"
files="StreamTask.java"/>
Expand Down Expand Up @@ -229,7 +234,8 @@

<suppress checks="CyclomaticComplexity"
files="(StreamsResetter|ProducerPerformance|Agent).java"/>

<suppress checks="BooleanExpressionComplexity"
files="StreamsResetter.java"/>
<suppress checks="NPathComplexity"
files="(ProducerPerformance|StreamsResetter|Agent).java"/>
<suppress checks="ImportControl"
Expand Down
28 changes: 27 additions & 1 deletion clients/src/main/java/org/apache/kafka/common/utils/Bytes.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,33 @@ private static String toString(final byte[] b, int off, int len) {
}

/**
* A byte array comparator based on lexicographic ordering.
* Increment the underlying byte array by adding 1. Throws an IndexOutOfBoundsException if incrementing would cause
* the underlying input byte array to overflow.
*
* @param input - The byte array to increment
* @return A new copy of the incremented byte array.
*/
public static Bytes increment(Bytes input) throws IndexOutOfBoundsException {
byte[] inputArr = input.get();
byte[] ret = new byte[inputArr.length];
int carry = 1;
for (int i = inputArr.length - 1; i >= 0; i--) {
if (inputArr[i] == (byte) 0xFF && carry == 1) {
ret[i] = (byte) 0x00;
} else {
ret[i] = (byte) (inputArr[i] + carry);
carry = 0;
}
}
if (carry == 0) {
return wrap(ret);
} else {
throw new IndexOutOfBoundsException();
}
}

/**
* A byte array comparator based on lexicograpic ordering.
*/
public final static ByteArrayComparator BYTES_LEXICO_COMPARATOR = new LexicographicByteArrayComparator();

Expand Down
84 changes: 84 additions & 0 deletions clients/src/test/java/org/apache/kafka/common/utils/BytesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.common.utils;

import org.junit.Test;

import java.util.Comparator;
import java.util.NavigableMap;
import java.util.TreeMap;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertEquals;

public class BytesTest {

@Test
public void testIncrement() {
byte[] input = new byte[]{(byte) 0xAB, (byte) 0xCD, (byte) 0xFF};
byte[] expected = new byte[]{(byte) 0xAB, (byte) 0xCE, (byte) 0x00};
Bytes output = Bytes.increment(Bytes.wrap(input));
assertArrayEquals(output.get(), expected);
}

@Test
public void testIncrementUpperBoundary() {
byte[] input = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
assertThrows(IndexOutOfBoundsException.class, () -> Bytes.increment(Bytes.wrap(input)));
}

@Test
public void testIncrementWithSubmap() {
final NavigableMap<Bytes, byte[]> map = new TreeMap<>();
Bytes key1 = Bytes.wrap(new byte[]{(byte) 0xAA});
byte[] val = new byte[]{(byte) 0x00};
map.put(key1, val);

Bytes key2 = Bytes.wrap(new byte[]{(byte) 0xAA, (byte) 0xAA});
map.put(key2, val);

Bytes key3 = Bytes.wrap(new byte[]{(byte) 0xAA, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF});
map.put(key3, val);

Bytes key4 = Bytes.wrap(new byte[]{(byte) 0xAB, (byte) 0x00});
map.put(key4, val);

Bytes key5 = Bytes.wrap(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01});
map.put(key5, val);

Bytes prefix = key1;
Bytes prefixEnd = Bytes.increment(prefix);

Comparator<? super Bytes> comparator = map.comparator();
final int result = comparator == null ? prefix.compareTo(prefixEnd) : comparator.compare(prefix, prefixEnd);
NavigableMap<Bytes, byte[]> subMapResults;
if (result > 0) {
//Prefix increment would cause a wrap-around. Get the submap from toKey to the end of the map
subMapResults = map.tailMap(prefix, true);
} else {
subMapResults = map.subMap(prefix, true, prefixEnd, false);
}

NavigableMap<Bytes, byte[]> subMapExpected = new TreeMap<>();
subMapExpected.put(key1, val);
subMapExpected.put(key2, val);
subMapExpected.put(key3, val);

assertEquals(subMapExpected.keySet(), subMapResults.keySet());
}
}
4 changes: 3 additions & 1 deletion core/src/main/scala/kafka/tools/StreamsResetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,9 @@ private boolean isInternalTopic(final String topicName) {
// Cf. https://issues.apache.org/jira/browse/KAFKA-7930
return !isInputTopic(topicName) && !isIntermediateTopic(topicName)
&& topicName.startsWith(options.valueOf(applicationIdOption) + "-")
&& (topicName.endsWith("-changelog") || topicName.endsWith("-repartition"));
&& (topicName.endsWith("-changelog") || topicName.endsWith("-repartition")
|| topicName.endsWith("-subscription-registration-topic")
|| topicName.endsWith("-subscription-response-topic"));
}

public static void main(final String[] args) {
Expand Down
88 changes: 88 additions & 0 deletions streams/src/main/java/org/apache/kafka/streams/kstream/KTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import org.apache.kafka.streams.state.QueryableStoreType;
import org.apache.kafka.streams.state.ReadOnlyKeyValueStore;

import java.util.function.Function;

/**
* {@code KTable} is an abstraction of a <i>changelog stream</i> from a primary-keyed table.
* Each record in this changelog stream is an update on the primary-keyed table with the record key as the primary key.
Expand Down Expand Up @@ -2117,6 +2119,92 @@ <VO, VR> KTable<K, VR> outerJoin(final KTable<K, VO> other,
final Named named,
final Materialized<K, VR, KeyValueStore<Bytes, byte[]>> materialized);

/**
*
* Join records of this [[KTable]] with another [[KTable]]'s records using non-windowed inner join. Records from this
* table are joined according to the result of keyExtractor on the other KTable.
*
* @param other the other {@code KTable} to be joined with this {@code KTable}. Keyed by KO.
* @param foreignKeyExtractor a {@link Function} that extracts the key (KO) from this table's value (V)
* @param joiner a {@link ValueJoiner} that computes the join result for a pair of matching records
* @param named a {@link Named} config used to name the processor in the topology
* @param materialized a {@link Materialized} that describes how the {@link StateStore} for the resulting {@code KTable}
* should be materialized. Cannot be {@code null}
* @param <VR> the value type of the result {@code KTable}
* @param <KO> the key type of the other {@code KTable}
* @param <VO> the value type of the other {@code KTable}
* @return
*/
<VR, KO, VO> KTable<K, VR> join(final KTable<KO, VO> other,
final Function<V, KO> foreignKeyExtractor,
final ValueJoiner<V, VO, VR> joiner,
final Named named,
final Materialized<K, VR, KeyValueStore<Bytes, byte[]>> materialized);

/**
*
* Join records of this [[KTable]] with another [[KTable]]'s records using non-windowed inner join. Records from this
* table are joined according to the result of keyExtractor on the other KTable.
*
* @param other the other {@code KTable} to be joined with this {@code KTable}. Keyed by KO.
* @param foreignKeyExtractor a {@link Function} that extracts the key (KO) from this table's value (V)
* @param joiner a {@link ValueJoiner} that computes the join result for a pair of matching records
* @param materialized a {@link Materialized} that describes how the {@link StateStore} for the resulting {@code KTable}
* should be materialized. Cannot be {@code null}
* @param <VR> the value type of the result {@code KTable}
* @param <KO> the key type of the other {@code KTable}
* @param <VO> the value type of the other {@code KTable}
* @return
*/
<VR, KO, VO> KTable<K, VR> join(final KTable<KO, VO> other,
Comment thread
bellemare marked this conversation as resolved.
final Function<V, KO> foreignKeyExtractor,
final ValueJoiner<V, VO, VR> joiner,
final Materialized<K, VR, KeyValueStore<Bytes, byte[]>> materialized);

/**
*
* Join records of this [[KTable]] with another [[KTable]]'s records using non-windowed left join. Records from this
* table are joined according to the result of keyExtractor on the other KTable.
*
* @param other the other {@code KTable} to be joined with this {@code KTable}. Keyed by KO.
* @param foreignKeyExtractor a {@link Function} that extracts the key (KO) from this table's value (V). If the
* * resultant foreignKey is null, the record will not propagate to the output.
* @param joiner a {@link ValueJoiner} that computes the join result for a pair of matching records
* @param named a {@link Named} config used to name the processor in the topology
* @param materialized a {@link Materialized} that describes how the {@link StateStore} for the resulting {@code KTable}
* should be materialized. Cannot be {@code null}
* @param <VR> the value type of the result {@code KTable}
* @param <KO> the key type of the other {@code KTable}
* @param <VO> the value type of the other {@code KTable}
* @return a {@code KTable} that contains only those records that satisfy the given predicate
*/
<VR, KO, VO> KTable<K, VR> leftJoin(final KTable<KO, VO> other,
final Function<V, KO> foreignKeyExtractor,
final ValueJoiner<V, VO, VR> joiner,
final Named named,
final Materialized<K, VR, KeyValueStore<Bytes, byte[]>> materialized);

/**
*
* Join records of this [[KTable]] with another [[KTable]]'s records using non-windowed left join. Records from this
* table are joined according to the result of keyExtractor on the other KTable.
*
* @param other the other {@code KTable} to be joined with this {@code KTable}. Keyed by KO.
* @param foreignKeyExtractor a {@link Function} that extracts the key (KO) from this table's value (V). If the
* resultant foreignKey is null, the record will not propagate to the output.
* @param joiner a {@link ValueJoiner} that computes the join result for a pair of matching records
* @param materialized a {@link Materialized} that describes how the {@link StateStore} for the resulting {@code KTable}
* should be materialized. Cannot be {@code null}
* @param <VR> the value type of the result {@code KTable}
* @param <KO> the key type of the other {@code KTable}
* @param <VO> the value type of the other {@code KTable}
* @return a {@code KTable} that contains only those records that satisfy the given predicate
*/
<VR, KO, VO> KTable<K, VR> leftJoin(final KTable<KO, VO> other,
Comment thread
bellemare marked this conversation as resolved.
final Function<V, KO> foreignKeyExtractor,
final ValueJoiner<V, VO, VR> joiner,
final Materialized<K, VR, KeyValueStore<Bytes, byte[]>> materialized);

/**
* Get the name of the local state store used that can be used to query this {@code KTable}.
*
Expand Down
Loading