Thank you, Matthias and Alieh, I've initiated a vote.
Sincerely, Hanyu On Fri, Oct 13, 2023 at 9:14 AM Matthias J. Sax <mj...@apache.org> wrote: > Thanks for pointing this out Alieh! I totally missed this. > > So I guess everything is settled and Hanyu can start a VOTE? > > For the KIP PR, we should ensure to update the JavaDocs to avoid > confusion in the future. > > > -Matthias > > On 10/12/23 12:21 PM, Alieh Saeedi wrote: > > Hi, > > just pointing to javadocs for range() and reverseRange(): > > > > range(): *@return The iterator for this range, from smallest to largest > > bytes.* > > reverseRange(): * @return The reverse iterator for this range, from > largest > > to smallest key bytes. > > > > Cheers, > > Alieh > > > > > > On Thu, Oct 12, 2023 at 7:32 AM Matthias J. Sax <mj...@apache.org> > wrote: > > > >> Quick addendum. > >> > >> Some DSL operator use `range` and seems to rely on ascending order, > >> too... Of course, if we say `range()` has no ordering guarantee, we > >> would add `forwardRange()` and let the DSL use `forwardRange`, too. > >> > >> The discussion of course also applies to `all()` and `reveserAll()`, and > >> and I assume also `prefixScan()` (even if there is no "reverse" version > >> for it). > >> > >> > >> On 10/11/23 10:22 PM, Matthias J. Sax wrote: > >>> Thanks for raising this question Hanyu. Great find! > >>> > >>> My interpretation is as follows (it's actually a warning signal that > the > >>> API contract is not better defined, and we should fix this by extending > >>> JavaDocs and docs on the web page about it). > >>> > >>> We have existing `range()` and `reverseRange()` methods on > >>> `ReadOnlyKeyValueStore` -- the interface itself is not typed (ie, just > >>> generics), and we state that we don't guarantee "logical order" because > >>> underlying stores are based on `byte[]` type. So far so... well. > >>> > >>> However, to make matters worse, we are also not explicit if the > >>> underlying store implementation *must* return keys is > >>> byte[]-lexicographical order or not... > >>> > >>> For `range()`, I would be kinda willing to accept that there is no > >>> ordering guarantee at all -- for example, if the underlying > byte[]-store > >>> is hash-based and implements a full scan to answer a `range()` it might > >>> not be efficient, but also not incorrect if keys are be returned in > some > >>> "random" (byte[]-)order. In isolation, I don't see an API contract > >>> violation. > >>> > >>> However, `reverseRange` implicitly states with its name, that some > >>> "descending order" (base on keys) is expected. Given the JavaDoc > comment > >>> about "logical" vs "byte[]" order, the contract (at least to me) is > >>> clear: returns records in descending byte[]-lexicographical order. -- > >>> Any other interpretation seems to be off? Curious to hear if you agree > >>> or disagree to this interpretation? > >>> > >>> > >>> > >>> If this is correct, it means we are actually lacking a API contract for > >>> ascending byte[]-lexicographical range scan. Furthermore, a hash-based > >>> byte[]-store would need to actually explicitly sort it's result for > >>> `reverseRange` to not violate the contract. > >>> > >>> To me, this raises the question if `range()` actually has a > >>> (non-explicit) contract about returning data in byte[]-lexicographical > >>> order? It seems a lot of people rely on this, and our default stores > >>> actually implement it this way. So if we don't look at `range()` in > >>> isolation, but look at the `ReadOnlyKeyValueStore` interface > >>> holistically, I would also buy the argument that `range()` implies > >>> "ascending "byte[]-lexicographical order". Thoughts? > >>> > >>> To be frank: to me, it's pretty clear that the original idea to add > >>> `range()` was to return data in ascending order. > >>> > >>> > >>> Question 1: > >>> - Do we believe that the range() contract is ascending > >>> byte[]-lexicographical order right now? > >>> > >>> If yes, I would propose to make it explicit in the JavaDocs. > >>> > >>> If no, I would also propose to make it explicit in the JavaDocs. > In > >>> addition, it raises the question if a method `forwardRange()` (for the > >>> lack of a better idea about a name right now) is actually missing to > >>> provide such a contract? > >>> > >>> > >>> Of course, we always depend on the serialization format for order, and > >>> if users need "logical order" they need to ensure to use a > serialization > >>> format that align byte[]-lexicographical order to logical order. But > for > >>> the scope of this work, I would not even try to open this can of > worms... > >>> > >>> > >>> > >>> > >>> Looking into `RangeQuery` the JavaDocs don't say anything about order. > >>> Thus, `RangeQuery#range()` could actually also be implemented by > calling > >>> `reverseRange()` without violating the contract as it seems. A > hash-base > >>> store could also implement it, without the need to explicitly sort... > >>> > >>> What brings be back to my original though about having three types of > >>> results for `Range` > >>> - no ordering guarantee > >>> - ascending (we would only give byte[]-lexicographical order) > >>> - descending (we would only give byte[]-lexicographical order) > >>> > >>> Again, I actually believe that the original intent of RangeQuery was to > >>> inherit the ascending order of `ReadOnlyKeyValueStore#range()`... > Please > >>> keep me honest about it. On the other hand, both APIs seems to be > >>> independent enough to not couple them... -- this could actually be a > >>> step into the right direction and would follow the underlying idea of > >>> IQv2 to begin with: decouple semantics for the store interfaces from > the > >>> query types and semantics... > >>> > >>> > >>> OR: we actually say that `RangeQuery#range` did implicitly inherit the > >>> (non explicit) "ascending byte[]-lexicographical" order of the > >>> underlying `ReadOnlyKeyValueStore`, and we just need to update the > >>> (Java)Docs to make it explicit. -- But it might go against the idea of > >>> IQv2 as stated above. > >>> > >>> > >>> Furthermore, the consequence would be, that a potential custom > >>> hash-based store, would need to do extra work to `range()` to do the > >>> sorting (or of course might reject the query as "not supported"). -- Of > >>> course, a hash-based store would still need to do extract work to > >>> implement `ReadOnlyKeyValueStore#(reverse)Range()` correctly (or also > >>> throw an `UnsupportedOperationException`... -- However, if we keep > store > >>> interface and query interface independent as intended by IQv2, we would > >>> allow a hash-based store to implement `RangeQuery#range()` even if the > >>> store does not support `ReadOnlyKeyValueStore#range()` (or only with > >>> additional sorting cost); such a decoupling sounds like an improvement > >>> to me. > >>> > >>> > >>> > >>> Sorry for the (too?) long email, but I wanted to be very explicit so we > >>> can hopefully settle this. Curious to hear your thought about it. > >>> > >>> > >>> > >>> -Matthias > >>> > >>> > >>> On 10/9/23 8:34 PM, Hanyu (Peter) Zheng wrote: > >>>> Thank you Colt, > >>>> > >>>> At first, we misinterpreted the JavaDoc. Upon further discussion, we > >>>> realized that after the key is converted to bytes, queries are based > >>>> on the > >>>> key's byte order, not its intrinsic order. > >>>> > >>>> Sincerely, > >>>> Hanyu > >>>> > >>>> On Mon, Oct 9, 2023 at 6:55 PM Colt McNealy <c...@littlehorse.io> > >> wrote: > >>>> > >>>>> Hanyu, > >>>>> > >>>>> I like the attention to detail! > >>>>> > >>>>> It is correct that the JavaDoc does not "guarantee" order. However, > the > >>>>> public API contract specified in the javadoc does mention > >>>>> lexicographical > >>>>> ordering of the bytes, which is a useful API contract. In our Streams > >>>>> app > >>>>> we make use of that contract during interactive queries > >>>>> (specifically, to > >>>>> guarantee correctness when doing a paginated range scan. If the order > >>>>> changes, then the "bookmark" we use for pagination would be > >>>>> meaningless). > >>>>> > >>>>> As such, I still think the KIP as you proposed is a highly useful > >>>>> feature. > >>>>> I would just make a note of the semantics in the JavaDoc and also in > >> the > >>>>> KIP. > >>>>> > >>>>> Thanks, > >>>>> Colt McNealy > >>>>> > >>>>> *Founder, LittleHorse.dev* > >>>>> > >>>>> > >>>>> On Mon, Oct 9, 2023 at 2:22 PM Hanyu (Peter) Zheng > >>>>> <pzh...@confluent.io.invalid> wrote: > >>>>> > >>>>>> After our discussion, we discovered something intriguing. The > >>>>>> definitions > >>>>>> for the range and reverseRange methods in the ReadOnlyKeyValueStore > >> are > >>>>> as > >>>>>> follows: > >>>>>> /** > >>>>>> * Get an iterator over a given range of keys. This iterator > >>>>>> must be > >>>>>> closed after use. > >>>>>> * The returned iterator must be safe from {@link > >>>>>> java.util.ConcurrentModificationException}s > >>>>>> * and must not return null values. > >>>>>> ** Order is not guaranteed as bytes lexicographical ordering > >>>>>> might > >>>>> not > >>>>>> represent key order.* > >>>>>> * > >>>>>> * @param from The first key that could be in the range, where > >>>>>> iteration starts from. > >>>>>> * A null value indicates that the range starts > >>>>>> with the > >>>>>> first element in the store. > >>>>>> * @param to The last key that could be in the range, where > >>>>> iteration > >>>>>> ends. > >>>>>> * A null value indicates that the range ends with > >> the > >>>>> last > >>>>>> element in the store. > >>>>>> * @return The iterator for this range, from smallest to > largest > >>>>> bytes. > >>>>>> * @throws InvalidStateStoreException if the store is not > >>>>>> initialized > >>>>>> */ > >>>>>> KeyValueIterator<K, V> range(K from, K to); > >>>>>> > >>>>>> /** > >>>>>> * Get a reverse iterator over a given range of keys. This > >>>>>> iterator > >>>>>> must be closed after use. > >>>>>> * The returned iterator must be safe from {@link > >>>>>> java.util.ConcurrentModificationException}s > >>>>>> * and must not return null values. > >>>>>> * *Order is not guaranteed as bytes lexicographical ordering > >>>>>> might > >>>>> not > >>>>>> represent key order.* > >>>>>> * > >>>>>> * @param from The first key that could be in the range, where > >>>>>> iteration ends. > >>>>>> * A null value indicates that the range starts > >>>>>> with the > >>>>>> first element in the store. > >>>>>> * @param to The last key that could be in the range, where > >>>>> iteration > >>>>>> starts from. > >>>>>> * A null value indicates that the range ends with > >> the > >>>>> last > >>>>>> element in the store. > >>>>>> * @return The reverse iterator for this range, from largest > to > >>>>>> smallest key bytes. > >>>>>> * @throws InvalidStateStoreException if the store is not > >>>>>> initialized > >>>>>> */ > >>>>>> default KeyValueIterator<K, V> reverseRange(K from, K to) { > >>>>>> throw new UnsupportedOperationException(); > >>>>>> } > >>>>>> > >>>>>> The query methods of RangeQuery ultimately invoke either the range > >>>>>> method > >>>>>> or the reverseRange method. However, as per the JavaDoc: the order > >>>>>> is not > >>>>>> guaranteed, since byte lexicographical ordering may not correspond > >>>>>> to the > >>>>>> actual key order. > >>>>>> > >>>>>> Sincerely, > >>>>>> Hanyu > >>>>>> > >>>>>> On Fri, Oct 6, 2023 at 10:00 AM Hanyu (Peter) Zheng > >>>>>> <pzh...@confluent.io > >>>>>> > >>>>>> wrote: > >>>>>> > >>>>>>> Thank you, Matthias, for the detailed implementation and > explanation. > >>>>> As > >>>>>>> of now, our capability is limited to executing interactive queries > on > >>>>>>> individual partitions. To illustrate: > >>>>>>> > >>>>>>> Consider the IQv2StoreIntegrationTest: > >>>>>>> > >>>>>>> We have two partitions: > >>>>>>> Partition0 contains key-value pairs: <0,0> and <2,2>. > >>>>>>> Partition1 contains key-value pairs: <1,1> and <3,3>. > >>>>>>> When executing RangeQuery.withRange(1,3), the results are: > >>>>>>> > >>>>>>> Partition0: [2] > >>>>>>> Partition1: [1, 3] > >>>>>>> To support functionalities like reverseRange and reverseAll, we can > >>>>>>> introduce the withDescendingKeys() method. For instance, using > >>>>>>> RangeQuery.withRange(1,3).withDescendingKeys(), the anticipated > >>>>>>> results > >>>>>> are: > >>>>>>> > >>>>>>> Partition0: [2] > >>>>>>> Partition1: [3, 1] > >>>>>>> > >>>>>>> In response to Hao's inquiry about the boundary issue, please refer > >> to > >>>>>> the > >>>>>>> StoreQueryUtils class. The code snippet: > >>>>>>> > >>>>>>> iterator = kvStore.range(lowerRange.orElse(null), > >>>>>> upperRange.orElse(null)); > >>>>>>> indicates that when implementing range in each store, it's > structured > >>>>>> like: > >>>>>>> > >>>>>>> @Override > >>>>>>> public KeyValueIterator<Bytes, byte[]> range(final Bytes from, > final > >>>>>> Bytes > >>>>>>> to) { > >>>>>>> if (from != null && to != null && from.compareTo(to) > 0) { > >>>>>>> This section performs the necessary checks. > >>>>>>> > >>>>>>> Sincerely, > >>>>>>> Hanyu > >>>>>>> > >>>>>>> On Thu, Oct 5, 2023 at 9:52 AM Hanyu (Peter) Zheng < > >>>>> pzh...@confluent.io> > >>>>>>> wrote: > >>>>>>> > >>>>>>>> Hi, Hao, > >>>>>>>> > >>>>>>>> In this case, it will return an empty set or list in the end. > >>>>>>>> > >>>>>>>> Sincerely, > >>>>>>>> Hanyu > >>>>>>>> > >>>>>>>> On Wed, Oct 4, 2023 at 10:29 PM Matthias J. Sax <mj...@apache.org > > > >>>>>> wrote: > >>>>>>>> > >>>>>>>>> Great discussion! > >>>>>>>>> > >>>>>>>>> It seems the only open question might be about ordering > guarantees? > >>>>>>>>> IIRC, we had a discussion about this in the past. > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> Technically (at least from my POV), existing `RangeQuery` does > not > >>>>> have > >>>>>>>>> a guarantee that data is return in any specific order (not even > on > >> a > >>>>>> per > >>>>>>>>> partitions bases). It just happens that RocksDB (and as pointed > out > >>>>> by > >>>>>>>>> Hanyu already, also the built-in in-memory store that is base on > a > >>>>>>>>> tree-map) allows us to return data ordered by key; as mentioned > >>>>>> already, > >>>>>>>>> this guarantee is limited on a per partition basis. > >>>>>>>>> > >>>>>>>>> If there would be custom store base on a hashed key-value store, > >>>>>>>>> this > >>>>>>>>> store could implement RangeQuery and return data (even for a > single > >>>>>>>>> partition) with no ordering, without violating the contract. > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> Thus, it could actually make sense, to extend `RangeQuery` and > >> allow > >>>>>>>>> three options: no-order, ascending, descending. For our existing > >>>>>>>>> Rocks/InMemory implementations, no-order could be equal to > >> ascending > >>>>>> and > >>>>>>>>> nothing changes effectively, but it might be a better API > contract? > >>>>> -- > >>>>>>>>> If we assume that there might be a custom hash-based store, such > a > >>>>>> store > >>>>>>>>> could reject a query if "ascending" is required, or might need to > >> do > >>>>>>>>> more work to implement it (up to the store maintainer). This is > >>>>>> actually > >>>>>>>>> the beauty of IQv2 that different stores can pick what queries > they > >>>>>> want > >>>>>>>>> to support. > >>>>>>>>> > >>>>>>>>> From an API contract point of view, it seems confusing to say: > >>>>>>>>> specifying nothing means no guarantee (or ascending if the store > >> can > >>>>>>>>> offer it), but descending can we explicitly request. Thus, a > >>>>> hash-based > >>>>>>>>> store, might be able to accept "order not specified query", but > >>>>>>>>> would > >>>>>>>>> reject "descending". This seems to be somewhat unbalanced? > >>>>>>>>> > >>>>>>>>> Thus, I am wondering if we should actually add > >>>>>>>>> `withAscendingKeys()`, > >>>>>>>>> too, even if it won't impact our current RocksDB/In-Memory > >>>>>>>>> implementations? > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> The second question is about per-partition or across-partition > >>>>>> ordering: > >>>>>>>>> it's not possible right now to actually offer across-partition > >>>>> ordering > >>>>>>>>> the way IQv2 is setup. The reason is, that the store that > >> implements > >>>>> a > >>>>>>>>> query type, is always a single shard. Thus, the implementation > does > >>>>> not > >>>>>>>>> have access to other shards. It's hard-coded inside Kafka > >>>>>>>>> Streams, to > >>>>>>>>> query each shared, and to "accumulate" partial results, and > return > >>>>> the > >>>>>>>>> back to the user. Note that the API is: > >>>>>>>>> > >>>>>>>>> > >>>>>>>>>> StateQueryResult<R> result = KafkaStreams.query(...); > >>>>>>>>>> Map<Integer, QueryResult<R>> resultPerPartitions = > >>>>>>>>> result.getPartitionResults(); > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> Thus, if we would want to offer across-partition ordering, we > >> cannot > >>>>> do > >>>>>>>>> it right now, because Kafka Streams does not know anything about > >> the > >>>>>>>>> semantics of the query it distributes... -- the result is an > >> unknown > >>>>>>>>> type <R>. We would need to extend IQv2 with an additional > >> mechanism, > >>>>>>>>> that allows users to plug in more custom code to "merge" multiple > >>>>>>>>> partitions result into a "global result". This is clearly > >>>>> out-of-scope > >>>>>>>>> for this KIP and would require a new KIP by itself. > >>>>>>>>> > >>>>>>>>> I seems that this contract, which is independent of the query > >>>>>>>>> type is > >>>>>>>>> not well understood, and thus a big +1 to fix the documentation. > I > >>>>>> don't > >>>>>>>>> think that this KIP must "define" anything, but it might of > >>>>>>>>> course be > >>>>>>>>> worth to add the explanation why the KIP cannot even offer > >>>>>>>>> global-ordering, as it's defined/limited by the IQv2 "framework" > >>>>>> itself, > >>>>>>>>> not the individual queries. > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> -Matthias > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> > >>>>>>>>> On 10/4/23 4:38 PM, Hao Li wrote: > >>>>>>>>>> Hi Hanyu, > >>>>>>>>>> > >>>>>>>>>> Thanks for the KIP! Seems there are already a lot of good > >>>>>> discussions. > >>>>>>>>> I > >>>>>>>>>> only have two comments: > >>>>>>>>>> > >>>>>>>>>> 1. Please make it clear in > >>>>>>>>>> ``` > >>>>>>>>>> /** > >>>>>>>>>> * Interactive range query using a lower and upper bound > to > >>>>>>>>> filter the > >>>>>>>>>> keys returned. > >>>>>>>>>> * @param lower The key that specifies the lower bound of > >> the > >>>>>>>>> range > >>>>>>>>>> * @param upper The key that specifies the upper bound of > >> the > >>>>>>>>> range > >>>>>>>>>> * @param <K> The key type > >>>>>>>>>> * @param <V> The value type > >>>>>>>>>> */ > >>>>>>>>>> public static <K, V> RangeQuery<K, V> withRange(final K > >>>>>>>>>> lower, > >>>>>>>>> final K > >>>>>>>>>> upper) { > >>>>>>>>>> return new RangeQuery<>(Optional.ofNullable(lower), > >>>>>>>>>> Optional.ofNullable(upper), true); > >>>>>>>>>> } > >>>>>>>>>> ``` > >>>>>>>>>> that a `null` in lower or upper parameter means it's unbounded. > >>>>>>>>>> 2. What's the behavior if lower is 3 and upper is 1? Is it > >>>>>>>>> IllegalArgument > >>>>>>>>>> or will this return an empty result? Maybe also clarify this in > >> the > >>>>>>>>>> document. > >>>>>>>>>> > >>>>>>>>>> Thanks, > >>>>>>>>>> Hao > >>>>>>>>>> > >>>>>>>>>> > >>>>>>>>>> On Wed, Oct 4, 2023 at 9:27 AM Hanyu (Peter) Zheng > >>>>>>>>>> <pzh...@confluent.io.invalid> wrote: > >>>>>>>>>> > >>>>>>>>>>> For testing purposes, we previously used a Set to record the > >>>>> results > >>>>>>>>> in > >>>>>>>>>>> IQv2StoreIntegrationTest. Let's take an example where we now > have > >>>>>> two > >>>>>>>>>>> partitions and four key-value pairs: <0,0> in p0, <1,1> in p1, > >>>>> <2,2> > >>>>>>>>> in p0, > >>>>>>>>>>> and <3,3> in p1. > >>>>>>>>>>> > >>>>>>>>>>> If we execute withRange(1,3), it will return a Set of <1, 2, > 3>. > >>>>>>>>> However, > >>>>>>>>>>> if we run withRange(1,3).withDescendingKeys(), and still use a > >>>>> Set, > >>>>>>>>> the > >>>>>>>>>>> result will again be a Set of <1,2,3>. This means we won't be > >> able > >>>>>> to > >>>>>>>>>>> determine whether the results have been reversed. > >>>>>>>>>>> > >>>>>>>>>>> To resolve this ambiguity, I've switched to using a List to > >> record > >>>>>> the > >>>>>>>>>>> results, ensuring the order of retrieval from partitions p0 and > >>>>> p1. > >>>>>>>>> So, > >>>>>>>>>>> withRange(1,3) would yield a List of [2, 1, 3], whereas > >>>>>>>>>>> withRange(1,3).withDescendingKeys() would produce a List of > >>>>> [2,3,1]. > >>>>>>>>>>> > >>>>>>>>>>> This ordering makes sense since RocksDB sorts its keys, and > >>>>>>>>> InMemoryStore > >>>>>>>>>>> uses a TreeMap structure, which means the keys are already > >> sorted. > >>>>>>>>>>> > >>>>>>>>>>> Sincerely, > >>>>>>>>>>> Hanyu > >>>>>>>>>>> > >>>>>>>>>>> On Wed, Oct 4, 2023 at 9:25 AM Hanyu (Peter) Zheng < > >>>>>>>>> pzh...@confluent.io> > >>>>>>>>>>> wrote: > >>>>>>>>>>> > >>>>>>>>>>>> Hi, Bruno > >>>>>>>>>>>> > >>>>>>>>>>>> Thank you for your suggestions, I will update them soon. > >>>>>>>>>>>> Sincerely, > >>>>>>>>>>>> > >>>>>>>>>>>> Hanyu > >>>>>>>>>>>> > >>>>>>>>>>>> On Wed, Oct 4, 2023 at 9:25 AM Hanyu (Peter) Zheng < > >>>>>>>>> pzh...@confluent.io> > >>>>>>>>>>>> wrote: > >>>>>>>>>>>> > >>>>>>>>>>>>> Hi, Lucas, > >>>>>>>>>>>>> > >>>>>>>>>>>>> Thank you for your suggestions. > >>>>>>>>>>>>> I will update the KIP and code together. > >>>>>>>>>>>>> > >>>>>>>>>>>>> Sincerely, > >>>>>>>>>>>>> Hanyu > >>>>>>>>>>>>> > >>>>>>>>>>>>> On Tue, Oct 3, 2023 at 8:16 PM Hanyu (Peter) Zheng < > >>>>>>>>> pzh...@confluent.io > >>>>>>>>>>>> > >>>>>>>>>>>>> wrote: > >>>>>>>>>>>>> > >>>>>>>>>>>>>> If we use WithDescendingKeys() to generate a RangeQuery to > do > >>>>>> the > >>>>>>>>>>>>>> reveseQuery, how do we achieve the methods like withRange, > >>>>>>>>>>> withUpperBound, > >>>>>>>>>>>>>> and withLowerBound only in this method? > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 8:01 PM Hanyu (Peter) Zheng < > >>>>>>>>>>> pzh...@confluent.io> > >>>>>>>>>>>>>> wrote: > >>>>>>>>>>>>>> > >>>>>>>>>>>>>>> I believe there's no need to introduce a method like > >>>>>>>>>>>>>>> WithDescendingKeys(). Instead, we can simply add a reverse > >>>>> flag > >>>>>> to > >>>>>>>>>>>>>>> RangeQuery. Each method within RangeQuery would then accept > >> an > >>>>>>>>>>> additional > >>>>>>>>>>>>>>> parameter. If the reverse is set to true, it would indicate > >>>>> the > >>>>>>>>>>> results > >>>>>>>>>>>>>>> should be reversed. > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> Initially, I introduced a reverse variable. When set to > >> false, > >>>>>> the > >>>>>>>>>>>>>>> RangeQuery class behaves normally. However, when reverse is > >>>>> set > >>>>>> to > >>>>>>>>>>> true, > >>>>>>>>>>>>>>> the RangeQuery essentially takes on the functionality of > >>>>>>>>>>> ReverseRangeQuery. > >>>>>>>>>>>>>>> Further details can be found in the "Rejected Alternatives" > >>>>>>>>> section. > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> In my perspective, RangeQuery is a class responsible for > >>>>>> creating > >>>>>>>>> a > >>>>>>>>>>>>>>> series of RangeQuery objects. It offers methods such as > >>>>>> withRange, > >>>>>>>>>>>>>>> withUpperBound, and withLowerBound, allowing us to generate > >>>>>>>>> objects > >>>>>>>>>>>>>>> representing different queries. I'm unsure how adding a > >>>>>>>>>>>>>>> withDescendingOrder() method would be compatible with the > >>>>> other > >>>>>>>>>>> methods, > >>>>>>>>>>>>>>> especially considering that, based on KIP 969, > >>>>>>>>> WithDescendingKeys() > >>>>>>>>>>> doesn't > >>>>>>>>>>>>>>> appear to take any input variables. And if > >>>>> withDescendingOrder() > >>>>>>>>>>> doesn't > >>>>>>>>>>>>>>> accept any input, how does it return a RangeQuery? > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 4:37 PM Hanyu (Peter) Zheng < > >>>>>>>>>>> pzh...@confluent.io> > >>>>>>>>>>>>>>> wrote: > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> Hi, Colt, > >>>>>>>>>>>>>>>> The underlying structure of inMemoryKeyValueStore is > >> treeMap. > >>>>>>>>>>>>>>>> Sincerely, > >>>>>>>>>>>>>>>> Hanyu > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 4:34 PM Hanyu (Peter) Zheng < > >>>>>>>>>>>>>>>> pzh...@confluent.io> wrote: > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> Hi Bill, > >>>>>>>>>>>>>>>>> 1. I will update the KIP in accordance with the PR and > >>>>>>>>> synchronize > >>>>>>>>>>>>>>>>> their future updates. > >>>>>>>>>>>>>>>>> 2. I will use that name. > >>>>>>>>>>>>>>>>> 3. you mean add something about ordering at the > motivation > >>>>>>>>> section? > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> Sincerely, > >>>>>>>>>>>>>>>>> Hanyu > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 4:29 PM Hanyu (Peter) Zheng < > >>>>>>>>>>>>>>>>> pzh...@confluent.io> wrote: > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> Hi, Walker, > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> 1. I will update the KIP in accordance with the PR and > >>>>>>>>> synchronize > >>>>>>>>>>>>>>>>>> their future updates. > >>>>>>>>>>>>>>>>>> 2. I will use that name. > >>>>>>>>>>>>>>>>>> 3. I'll provide additional details in that section. > >>>>>>>>>>>>>>>>>> 4. I intend to utilize rangeQuery to achieve what we're > >>>>>>>>> referring > >>>>>>>>>>> to > >>>>>>>>>>>>>>>>>> as reverseQuery. In essence, reverseQuery is merely a > >> term. > >>>>>> To > >>>>>>>>>>> clear up any > >>>>>>>>>>>>>>>>>> ambiguity, I'll make necessary adjustments to the KIP. > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> Sincerely, > >>>>>>>>>>>>>>>>>> Hanyu > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 4:09 PM Hanyu (Peter) Zheng < > >>>>>>>>>>>>>>>>>> pzh...@confluent.io> wrote: > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> Ok, I will change it back to following the code, and > >>>>> update > >>>>>>>>> them > >>>>>>>>>>>>>>>>>>> together. > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 2:27��PM Walker Carlson > >>>>>>>>>>>>>>>>>>> <wcarl...@confluent.io.invalid> wrote: > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> Hello Hanyu, > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> Looking over your kip things mostly make sense but I > >>>>> have a > >>>>>>>>>>> couple > >>>>>>>>>>>>>>>>>>>> of > >>>>>>>>>>>>>>>>>>>> comments. > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> 1. You have "withDescandingOrder()". I think you > >>>>>>>>>>>>>>>>>>>> mean > >>>>>>>>>>>>>>>>>>>> "descending" :) > >>>>>>>>>>>>>>>>>>>> Also there are still a few places in the do > where > >>>>>>>>>>>>>>>>>>>> its > >>>>>>>>> called > >>>>>>>>>>>>>>>>>>>> "setReverse" > >>>>>>>>>>>>>>>>>>>> 2. Also I like "WithDescendingKeys()" better > >>>>>>>>>>>>>>>>>>>> 3. I'm not sure of what ordering guarantees we > are > >>>>>>>>> offering. > >>>>>>>>>>>>>>>>>>>> Perhaps we > >>>>>>>>>>>>>>>>>>>> can add a section to the motivation clearly > >> spelling > >>>>>> out > >>>>>>>>> the > >>>>>>>>>>>>>>>>>>>> current > >>>>>>>>>>>>>>>>>>>> ordering and the new offering? > >>>>>>>>>>>>>>>>>>>> 4. When you say "use unbounded reverseQuery to > >>>>> achieve > >>>>>>>>>>>>>>>>>>>> reverseAll" do > >>>>>>>>>>>>>>>>>>>> you mean "use unbounded RangeQuery to achieve > >>>>>>>>> reverseAll"? as > >>>>>>>>>>>>>>>>>>>> far as I can > >>>>>>>>>>>>>>>>>>>> tell we don't have a reverseQuery as a named > >> object? > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> Looking good so far > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> best, > >>>>>>>>>>>>>>>>>>>> Walker > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 2:13 PM Colt McNealy < > >>>>>>>>> c...@littlehorse.io > >>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> wrote: > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> Hello Hanyu, > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> Thank you for the KIP. I agree with Matthias' > proposal > >>>>> to > >>>>>>>>> keep > >>>>>>>>>>>>>>>>>>>> the naming > >>>>>>>>>>>>>>>>>>>>> convention consistent with KIP-969. I favor the > >>>>>>>>>>>>>>>>>>>> `.withDescendingKeys()` > >>>>>>>>>>>>>>>>>>>>> name. > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> I am curious about one thing. RocksDB guarantees that > >>>>>>>>> records > >>>>>>>>>>>>>>>>>>>> returned > >>>>>>>>>>>>>>>>>>>>> during a range scan are lexicographically ordered by > >> the > >>>>>>>>> bytes > >>>>>>>>>>>>>>>>>>>> of the keys > >>>>>>>>>>>>>>>>>>>>> (either ascending or descending order, as specified > in > >>>>> the > >>>>>>>>>>>>>>>>>>>> query). This > >>>>>>>>>>>>>>>>>>>>> means that results within a single partition are > indeed > >>>>>>>>>>>>>>>>>>>> ordered.** My > >>>>>>>>>>>>>>>>>>>>> reading of KIP-805 suggests to me that you don't need > >> to > >>>>>>>>>>> specify > >>>>>>>>>>>>>>>>>>>> the > >>>>>>>>>>>>>>>>>>>>> partition number you are querying in IQv2, which > means > >>>>>> that > >>>>>>>>> you > >>>>>>>>>>>>>>>>>>>> can have a > >>>>>>>>>>>>>>>>>>>>> valid reversed RangeQuery over a store with "multiple > >>>>>>>>>>>>>>>>>>>> partitions" in it. > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> Currently, IQv1 does not guarantee order of keys in > >> this > >>>>>>>>>>>>>>>>>>>> scenario. Does > >>>>>>>>>>>>>>>>>>>>> IQv2 support ordering across partitions? Such an > >>>>>>>>> implementation > >>>>>>>>>>>>>>>>>>>> would > >>>>>>>>>>>>>>>>>>>>> require opening a rocksdb range scan** on multiple > >>>>> rocksdb > >>>>>>>>>>>>>>>>>>>> instances (one > >>>>>>>>>>>>>>>>>>>>> per partition), and polling the first key of each. > >>>>> Whether > >>>>>>>>> or > >>>>>>>>>>>>>>>>>>>> not this is > >>>>>>>>>>>>>>>>>>>>> ordered, could we please add that to the > documentation? > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> **(How is this implemented/guaranteed in an > >>>>>>>>>>>>>>>>>>>> `inMemoryKeyValueStore`? I > >>>>>>>>>>>>>>>>>>>>> don't know about that implementation). > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> Colt McNealy > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> *Founder, LittleHorse.dev* > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 1:35 PM Hanyu (Peter) Zheng > >>>>>>>>>>>>>>>>>>>>> <pzh...@confluent.io.invalid> wrote: > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> ok, I will update it. Thank you Matthias > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> Sincerely, > >>>>>>>>>>>>>>>>>>>>>> Hanyu > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> On Tue, Oct 3, 2023 at 11:23 AM Matthias J. Sax < > >>>>>>>>>>>>>>>>>>>> mj...@apache.org> > >>>>>>>>>>>>>>>>>>>>> wrote: > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> Thanks for the KIP Hanyu! > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> I took a quick look and it think the proposal makes > >>>>>> sense > >>>>>>>>>>>>>>>>>>>> overall. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> A few comments about how to structure the KIP. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> As you propose to not add `ReverseRangQuery` class, > >>>>> the > >>>>>>>>>>> code > >>>>>>>>>>>>>>>>>>>> example > >>>>>>>>>>>>>>>>>>>>>>> should go into "Rejected Alternatives" section, not > >> in > >>>>>> the > >>>>>>>>>>>>>>>>>>>> "Proposed > >>>>>>>>>>>>>>>>>>>>>>> Changes" section. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> For the `RangeQuery` code example, please omit all > >>>>>>>>> existing > >>>>>>>>>>>>>>>>>>>> methods > >>>>>>>>>>>>>>>>>>>>> etc, > >>>>>>>>>>>>>>>>>>>>>>> and only include what will be added/changed. This > >> make > >>>>>> it > >>>>>>>>>>>>>>>>>>>> simpler to > >>>>>>>>>>>>>>>>>>>>>>> read the KIP. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> nit: typo > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>>> the fault value is false > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> Should be "the default value is false". > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> Not sure if `setReverse()` is the best name. Maybe > >>>>>>>>>>>>>>>>>>>>> `withDescandingOrder` > >>>>>>>>>>>>>>>>>>>>>>> (or similar, I guess `withReverseOrder` would also > >>>>> work) > >>>>>>>>>>>>>>>>>>>> might be > >>>>>>>>>>>>>>>>>>>>>>> better? Would be good to align to KIP-969 proposal > >>>>> that > >>>>>>>>>>>>>>>>>>>> suggest do use > >>>>>>>>>>>>>>>>>>>>>>> `withDescendingKeys` methods for "reverse > key-range"; > >>>>> if > >>>>>>>>> we > >>>>>>>>>>>>>>>>>>>> go with > >>>>>>>>>>>>>>>>>>>>>>> `withReverseOrder` we should change KIP-969 > >>>>> accordingly. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> Curious to hear what others think about naming this > >>>>>>>>>>>>>>>>>>>> consistently across > >>>>>>>>>>>>>>>>>>>>>>> both KIPs. > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> -Matthias > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> On 10/3/23 9:17 AM, Hanyu (Peter) Zheng wrote: > >>>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://cwiki.apache.org/confluence/display/KAFKA/KIP-985%3A+Add+reverseRange+and+reverseAll+query+over+kv-store+in+IQv2 > >>>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>>>>>>>>> < > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>>>>>>>>>>>>> [image: > >>>>>>>>>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > >>>>>>>>> LinkedIn] > >>>>>>>>>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/ > >>>>>> [image: > >>>>>>>>>>> Slack] > >>>>>>>>>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: > >>>>>> YouTube] > >>>>>>>>>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>>>>>>>>> < > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > >>>>>> LinkedIn] > >>>>>>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/ > >[image: > >>>>>>>>> Slack] > >>>>>>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: > >> YouTube] > >>>>>>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > >>>>> LinkedIn] > >>>>>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > >>>>>> Slack] > >>>>>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: > YouTube] > >>>>>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > >>>>> LinkedIn] > >>>>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > >>>>>> Slack] > >>>>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: > YouTube] > >>>>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > >> LinkedIn] > >>>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > >>>>> Slack] > >>>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> -- > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > LinkedIn] > >>>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > >>>>> Slack] > >>>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>>> > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> -- > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: > LinkedIn] > >>>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > >> Slack] > >>>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>>> > >>>>>>>>>>>>> > >>>>>>>>>>>>> > >>>>>>>>>>>>> -- > >>>>>>>>>>>>> > >>>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > Slack] > >>>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>>> > >>>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>>> > >>>>>>>>>>>> > >>>>>>>>>>>> > >>>>>>>>>>>> -- > >>>>>>>>>>>> > >>>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: > Slack] > >>>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>>> > >>>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>>>> -- > >>>>>>>>>>> > >>>>>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>>>>> Software Engineer Intern > >>>>>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>>>>> Follow us: [image: Blog] > >>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>>>>>>> [image: > >>>>>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: Slack] > >>>>>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>>>>> <https://youtube.com/confluent> > >>>>>>>>>>> > >>>>>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>>>>> < > >>>>>>>>>>> > >>>>>>>>> > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>>>>>>> > >>>>>>>>>>> > >>>>>>>>>> > >>>>>>>>> > >>>>>>>> > >>>>>>>> > >>>>>>>> -- > >>>>>>>> > >>>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>>> Software Engineer Intern > >>>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>>> Follow us: [image: Blog] > >>>>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>> [image: > >>>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: Slack] > >>>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>>> <https://youtube.com/confluent> > >>>>>>>> > >>>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>> > >>>>>>>> > >>>>>>> > >>>>>>> > >>>>>>> -- > >>>>>>> > >>>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>>> Software Engineer Intern > >>>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>>> Follow us: [image: Blog] > >>>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>> [image: > >>>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: Slack] > >>>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>>> <https://youtube.com/confluent> > >>>>>>> > >>>>>>> [image: Try Confluent Cloud for Free] > >>>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>> > >>>>>>> > >>>>>> > >>>>>> > >>>>>> -- > >>>>>> > >>>>>> [image: Confluent] <https://www.confluent.io> > >>>>>> Hanyu (Peter) Zheng he/him/his > >>>>>> Software Engineer Intern > >>>>>> +1 (213) 431-7193 <+1+(213)+431-7193> > >>>>>> Follow us: [image: Blog] > >>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog > >>>>>>> [image: > >>>>>> Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] > >>>>>> <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: Slack] > >>>>>> <https://slackpass.io/confluentcommunity>[image: YouTube] > >>>>>> <https://youtube.com/confluent> > >>>>>> > >>>>>> [image: Try Confluent Cloud for Free] > >>>>>> < > >>>>>> > >>>>> > >> > https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic > >>>>>>> > >>>>>> > >>>>> > >>>> > >>>> > >> > > > -- [image: Confluent] <https://www.confluent.io> Hanyu (Peter) Zheng he/him/his Software Engineer Intern +1 (213) 431-7193 <+1+(213)+431-7193> Follow us: [image: Blog] <https://www.confluent.io/blog?utm_source=footer&utm_medium=email&utm_campaign=ch.email-signature_type.community_content.blog>[image: Twitter] <https://twitter.com/ConfluentInc>[image: LinkedIn] <https://www.linkedin.com/in/hanyu-peter-zheng/>[image: Slack] <https://slackpass.io/confluentcommunity>[image: YouTube] <https://youtube.com/confluent> [image: Try Confluent Cloud for Free] <https://www.confluent.io/get-started?utm_campaign=tm.fm-apac_cd.inbound&utm_source=gmail&utm_medium=organic>