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
> >>>>>
> >>>>
> >>>
> >>
> >>
>

Reply via email to