Thanks! SGTM.

Seems all open questions are resolved. Thanks for pushing this through!

-Matthias

On 11/21/23 2:29 AM, Alieh Saeedi wrote:
Yes Matthias,
Based on the discussion we had, it has now been changed to Optional and the
default is empty (for the latest). Also, the `validTo()` method returns an
Optional.

Cheers,
Alieh

On Mon, Nov 20, 2023 at 7:38 PM Matthias J. Sax <mj...@apache.org> wrote:

I think we should also discuss a little more about `validTo()` method?

Given that "latest" version does not have a valid-to TS, should we
change the return type to `Optional` and return `empty()` for "latest"?

ATM the KIP uses `MAX_VALUE` for "latest" what seems to be less clean?
We could also use `-1` (unknown), but both might be less expressive than
`Optional`?


-Matthias

On 11/20/23 1:59 AM, Bruno Cadonna wrote:
Hi Alieh,

Although, I've already voted, I found a minor miss. You should also add
a method isDescending() since the results could also be unordered now
that we agreed that the results are unordered by default. If both --
isDescending() and isAscending -- are false neither
withDescendingTimestamps() nor withAscendingTimestamps() was called.

Best,
Bruno

On 11/17/23 11:25 AM, Alieh Saeedi wrote:
Hi all,
Thank you for the feedback.

So we agreed on no default ordering for keys and TSs. So I must provide
both withAscendingXx() and with DescendingXx() for the class.
Apart from that, I think we can either remove the existing constructor
for
the `VersionedRecord` class or follow the `Optional` thing.

Since many hidden aspects of the KIP are quite clear now and we have
come
to a consensus about them, I think it 's time to vote ;-)
I look forward to your votes. Thanks a lot.

Cheers,
Alieh

On Fri, Nov 17, 2023 at 2:27 AM Matthias J. Sax <mj...@apache.org>
wrote:

Thanks, Alieh.

Overall SGTM. About `validTo` -- wondering if we should make it an
`Optional` and set to `empty()` by default?

I am totally ok with going with the 3-way option about ordering using
default "undefined". For this KIP (as it's all net new) nothing really
changes. -- However, we should amend `RangeQuery`/KIP-985 to align it.

Btw: so far we focused on key-ordering, but I believe the same
"ordering
undefined by default" would apply to time-ordering, too? This might
affect KIP-997, too.


-Matthias

On 11/16/23 12:51 AM, Bruno Cadonna wrote:
Hi,

80)
We do not keep backwards compatibility with IQv1, right? I would even
say that currently we do not need to keep backwards compatibility
among
IQv2 versions since we marked the API "Evolving" (do we only mean code
compatibility here or also behavioral compatibility?). I propose to
try
to not limit ourselves for backwards compatibility that we explicitly
marked as evolving.
I re-read the discussion on KIP-985. In that discussion, we were quite
focused on what the state store provides. I see that for range
queries,
we have methods on the state store interface that specify the order,
but
that should be kind of orthogonal to the IQv2 query type. Let's assume
somebody in the future adds a state store implementation that is not
order based. To account for use cases where the order does not matter,
this person might also add a method to the state store interface that
does not guarantee any order. However, our range query type is
specified
to guarantee order by default. So we need to add something like
withNoOrder() to the query type to allow the use cases that does not
need order and has the better performance in IQ. That does not look
very
nice to me. Having the no-order-guaranteed option does not cost us
anything and it keeps the IQv2 interface flexible. I assume we want to
drop the Evolving annotation at some point.
Sorry for not having brought this up in the discussion about KIP-985.

Best,
Bruno





On 11/15/23 6:56 AM, Matthias J. Sax wrote:
Just catching up on this one.


50) I am also in favor of setting `validTo` in VersionedRecord for
single-key single-ts lookup; it seems better to return the proper
timestamp. The timestamp is already in the store and it's cheap to
extract it and add to the result, and it might be valuable
information
for the user. Not sure though if we should deprecate the existing
constructor though, because for "latest" it's convenient to have?


60) Yes, I meant `VersionedRecord`. Sorry for the mixup.


80) We did discuss this question on KIP-985 (maybe you missed it
Bruno). It's kinda tricky.

Historically, it seems that IQv1, ie, the `ReadOnlyXxx` interfaces
provide a clear contract that `range()` is ascending and
`reverseRange()` is descending.

For `RangeQuery`, the question is, if we did implicitly inherit this
contract? Our conclusion on KIP-985 discussion was, that we did
inherit it. If this holds true, changing the contract would be a
breaking change (what might still be acceptable, given that the
interface is annotated as unstable, and that IQv2 is not widely
adopted yet). I am happy to go with the 3-option contract, but just
want to ensure we all agree it's the right way to go, and we are
potentially willing to pay the price of backward incompatibility.



Do we need a snapshot semantic or can we specify a weaker but still
useful semantic?

I don't think we necessarily need it, but as pointed out by Lucas,
all
existing queries provide it. Overall, my main point is really about
not implementing something "random", but defining a proper binding
contract that allows users to reason about it.

I general, I agree that weaker semantics might be sufficient, but I
am
not sure if we can implement anything weaker in a reasonable way?
Happy to be convinced otherwise. (I have some example, that I will
omit for now, as I hope we can actually go with snapshot semantics.)

The RocksDB Snaptshot idea from Lucas sounds very promising. I was
not
aware that we could do this with RocksDB (otherwise I might have
suggested it on the PR right away). I guess the only open question
remaining would be, if we can provide the same guarantees for a
future
in-memory implementation for VersionedStores? It sounds possible to
do, but we should have some level of confidence about it?


-Matthias

On 11/14/23 6:33 AM, Lucas Brutschy wrote:
Hi Alieh,

I agree with Bruno that a weaker guarantee could be useful already,
and it's anyway possible to strengthen the guarantees later on. On
the
other hand, it would be nice to provide a consistent view across all
segments if it doesn't come with major downsides, because until
now IQ
does provide a consistent view (via iterators), and this would be
the
first feature that diverges from that guarantee.

I think a consistent could be achieved relatively easily by
creating a
snapshot (https://github.com/facebook/rocksdb/wiki/Snapshot) that
will
ensure a consistent view on a single DB and using
`ReadOptions.setSnapshot` for the gets. Since versioned state stores
segments seem to be backed by a single RocksDB instance (that was
unclear to me during our earlier discussion), a single snapshot
should
be enough to guarantee a consistent view - we just need to make sure
to clean up the snapshots after use, similar to iterators. If we
instead need a consistent view across multiple RocksDB instances, we
may have to acquire a write lock on all of those (could use the
current object monitors of our `RocksDB` interface) for the duration
of creating snapshots across all instances - which I think would
also
be permissible performance-wise. Snapshots are just a sequence
number
and should be pretty lightweight to create (they have, however,
downside when it comes to compaction just like iterators).

With that in mind, I would be in favor of at least exploring the
option of using snapshots for a consistent view here, before
dropping
this useful guarantee.

Cheers,
Lucas

On Tue, Nov 14, 2023 at 2:20 PM Bruno Cadonna <cado...@apache.org>
wrote:

Hi Alieh,

Regarding the semantics/guarantees of the query type:

Do we need a snapshot semantic or can we specify a weaker but still
useful semantic?

An option could be to guarantee that:
1. the query returns the latest version when the query arrived at
the
state store
2. the query returns a valid history, i.e., versions with
adjacent and
non-overlapping validity intervals.

I think guarantee 1 is quite obvious. Guarantee 2 maybe needs some
explanation. If we do not avoid writes to the state store during
the
processing of interactive queries, it might for example happen that
the
latest version in the state store moves to data structures that are
responsible for older versions. In our RocksDB implementation
that are
the segments. Thus, it could be that during query processing Kafka
Streams reads the latest value x and encounters again x in a
segment
because a processor put a newer version of x in the versioned state
store. A similar scenario might also happen to earlier versions. If
Streams does not account for such cases it could return invalid
histories.

Maybe such weaker guarantees are enough for most use cases.

You could consider implementing weaker guarantees as I described
and
if
there is demand propose stricter guarantees in a follow-up KIP.

Maybe there are also other simpler guarantees that make sense.

Best,
Bruno


On 11/9/23 12:30 PM, Bruno Cadonna wrote:
Hi,

Thanks for the updates!

First my take on previous comments:


50)
I am in favor of deprecating the constructor that does not take
the
validTo parameter. That implies that I am in favor of modifying
get(key,
asOf) to set the correct validTo.


60)
I am in favor of renaming ValueIterator to VersionedRecordIterator
and
define it as:

public interface VersionedRecordIterator<V> extends
Iterator<VersionedRecord<V>>

(Matthias, you mixed up ValueAndTimestamp with VersionedRecord in
your
last e-mail, didn't you? Just double-checking if I understood what
you
are proposing.)


70)
I agree with Matthias that adding a new method on the
VersionedKeyValueStore interface defeats the purpose of one of the
goals
of IQv2, i.e., not to need to extend the state store interface for
IQ. I
would say if we do not need the method in normal processing, we
should
not extend the public state store interface. BTW, nobody forces
you
to
StoreQueryUtils. I think that internal utils class was
introduced for
convenience to leverage existing methods on the state store
interface.


80)
Why do we limit ourselves by specifying a default order on the
result?
Different state store implementation might have different
strategies to
store the records which affects the order in which the records are
returned if they are not sorted before they are returned to the
user.
Some users might not be interested in an order of the result and
so
there is no reason those users pay the cost for sorting. I
propose to
not specify a default order but sort the results (if needed) when
withDescendingX() and withAscendingX() is specified on the query
object.


Regarding the snapshot guarantees for the iterators, I need to
think
a
bit more about it. I will come back to this thread in the next
days.


Best,
Bruno


On 11/8/23 5:30 PM, Alieh Saeedi wrote:
Thank you, Bruno and Matthias, for keeping the discussion going
and for
reviewing the PR.

Here are the KIP updates:

       - I removed the `peek()` from the `ValueIterator` interface
since
we do
       not need it.
       - Yes, Bruno, the `validTo` field in the `VersionedRecod`
class is
       exclusive. I updated the javadocs for that.


Very important critical open questions. I list them here based on
priority
(descendingly).

       - I implemented the `get(key, fromtime, totime)` method
here

<

https://github.com/aliehsaeedii/kafka/blob/9578b7cb7cdade22cc63f671693f5aeb993937ca/streams/src/main/java/org/apache/kafka/streams/state/internals/RocksDBVersionedStore.java#L262
:
       the problem is that this implementation does not guarantee
consistency
       because processing might continue interleaved (no snapshot
semantics is
       implemented). More over, it materializes all results in
memory.
          - Solution 1: Use a lock and release it after
retrieving all
desired
          records from all segments.
             - positive point: snapshot semantics is implemented
             - negative points: 1) It is expensive since iterating
over all
             segments may take a long time. 2) It still requires
materializing results
             on memory
          - Solution 2: use `RocksDbIterator`.
             - positive points: 1) It guarantees snapshot
segments.
2) It
does
             not require materializing results in memory.
             - negative points: it is expensive because, anyway,
we
need to
             iterate over all (many) segments.

               Do you have any thoughts on this issue? (ref:
Matthias's
comment
<

https://github.com/apache/kafka/pull/14626#pullrequestreview-1709280589>)

       - I added the field `validTo` in `VersionedRecord`. Its
default
value is
       MAX. But as Matthias mentioned, for the single-key
single-ts
       (`VersionedKeyQuery` in KIP-960), it may not always be
true.
If the
       returned record belongs to an old segment, maybe it is not
valid
any more.
       So MAX is not the correct value for `ValidTo`. Two
solutions
come
to mind:
          - Solution 1: make the `ValidTo` as an `Optional` and
set
it to
          `empty` for the retuned result of `get(key,
asOfTimestamp)`.
          - Solution 2: change the implementation of `get(key,
asOfTimestamp)`
          so that it finds the correct `validTo` for the returned
versionedRecord.

          - In this KIP and the next one, even though the default
ordering is
       with ascending ts, I added the method
`withAscendingTimestamps()`
to have
       more user readibility (as Bruno suggested), while Hanyu did
only add
       `withDescending...` methods (he did not need ascending
because
that's the
       default anyway). Matthias believes that we should not have
       inconsistencies (he actually hates them:D). Shall I
change my
KIP
or Hanyu?
       Thoughts?


That would be maybe helpful to look into the PR
<https://github.com/apache/kafka/pull/14626> for more clarity
and
even
review that ;-)

Cheers,
Alieh

On Thu, Nov 2, 2023 at 7:13 PM Bruno Cadonna <cado...@apache.org

wrote:

Hi Alieh,

First of all, I like the examples.

Is validTo in VersionedRecord exclusive or inclusive?
In the javadocs you write:

"@param validTo    the latest timestamp that value is valid"

I think that is not true because the validity is defined by the
start
time of the new version. The new and the old version cannot
both be
valid at that same time.

Theoretically, you could set validTo to the start time of the
new
version - 1. However, what is the unit of the 1? Is it
nanoseconds?
Milliseconds? Seconds? Sure we could agree on one, but I think
it
is
more elegant to just make the validTo exclusive. Actually, you
used it
as exclusive in your examples.


Thanks for the KIP!

Best,
Bruno

On 11/1/23 9:01 PM, Alieh Saeedi wrote:
Hi all,
@Matthias: I think Victoria was right. I must add the method
`get(key,
fromTime, toTime)` to the interface `VersionedKeyValueStore`.
Right
now,
having the method only in `RocksDBVersionedStore`, made me to
have an
instance of `RocksDBVersionedStore` (instead of
`VersionedKeyValueStore`)
in `StoreQueryUtils.runMultiVersionedKeyQuery()` method. In
future, we
are
going to use the same method for In-memory/SPDB/blaBla
versioned
stores.
Then either this method won't work any more, or we have to add
code (if
clauses) for each type of versioned stores. What do you think
about
that?

Bests,
Alieh

On Tue, Oct 24, 2023 at 10:01 PM Alieh Saeedi
<asae...@confluent.io>
wrote:

Thank you, Matthias, Bruno, and Guozhang for keeping the
discussion
going.

Here is the list of changes I made:

        1. I enriched the "Example" section as Bruno suggested.
Do you
please
        have a look at that section? I think I devised an
interesting one
;-)
        2. As Matthias and Guozhang suggested, I renamed
variables and
methods
        as follows:
           - "fromTimestamp" -> "fromTime"
           - "asOfTimestamp" -> "toTime"
           - "from(Instant fromTime)" -> "fromTime(Instant
fromTime)"
           - "asOf(Instant toTime)" -> "toTime(Instant toTime)"
        3.

        About throwing an NPE when time bounds are `null`: Ok,
Matthias, I
am
        convinced by your idea. I consider a null timestamp
as "no
bound".
But I
        had to change KIP-960 and the corresponding PR to be
consistent as
well.

Answering questions and some more discussion points:

        1.

        For now, I keep the class names as they are.
        2.

        About the new field "validTo" in VersionedRecord. Yes
Matthias I
keep
        the old constructor, which does not have `validTo` as
an
input
parameter.
        But in the body of the old constructor, I consider the
default
value MAX
        for the validTo field. I think MAX must be the default
value for
`validTo`
        since before inserting a tombstone or a new value for
the
same
key,
the
        value must be valid till iternity.
        3.

        Regarding changing the ValueIterator interface from
`public
interface
        ValueIterator<V> extends Iterator<V>` to `public
interface
        ValueIterator<V> extends Iterator<VersionedRecord<V>>`:
Matthias, I
do not
        know how it improves type safety because the
MultiVersionedKeyQuery
class
        returns a ValueIterator of VersionedRecord any way. But
if we
want
to be
        consistent with KeyValueIterator, we must apply the
changes you
suggested.
        4.

        Regarding adding the new `get(key, fromTime, toTime)`
method
to the
        public interface `VersionedKeyValueStore` or only
adding
it to
the
        class `RocksDBVersionedStore`: In the KIP, I changed
the
interface
as
        Victoria suggested. But still, I am not convinced why
we
do that.
@Victoria:
        Do you please clarify why we have to define it in the
interface?
More
        specifically, why do we need to use generic
VersionedKeyValueStore
        instead of simply using the implementing classes?

Cheers,
Alieh

On Sat, Oct 14, 2023 at 3:35 AM Guozhang Wang <
guozhang.wang...@gmail.com>
wrote:

Thanks Alieh for the KIP, as well as a nice summary of all
the
discussions! Just my 2c regarding your open questions:

1. I just checked KIP-889
(



https://cwiki.apache.org/confluence/display/KAFKA/KIP-889%3A+Versioned+State+Stores
)
and we used "VersionedRecord<V> get(K key, long
asOfTimestamp);",
so I
feel to be consistent with this API is better compared with
being
consistent with "WindowKeyQuery"?

3. I agree with Matthias that naming is always tricky, and I
also
tend
to be consistent with what we already have (even if in retro
it may
not be the best idea :P and if that was really becoming a
complaint,
we would change it across the board in one shot as well
later).

Guozhang

On Wed, Oct 11, 2023 at 9:12 PM Matthias J. Sax
<mj...@apache.org>
wrote:

Thanks for the update!



Some thoughts about changes you made and open questions you
raised:


10) About asOf vs until: I was just looking into
`WindowKeyQuery`,
`WindowRangeQuery` and also `ReadOnlyWindowStore`
interfaces.
For
those,
we use "timeFrom" and "timeTo", so it seems best to actually
use
`to(Instant toTime)` to keep the naming consistent across
the
board?

If yes, we should also do `from (Instant fromTime)` and use
getters
`fromTime()` and `toTime()` -- given that it's range bounds
it seems
acceptable to me, to diverge a little bit from KIP-960
`asOfTimestamp()`
-- but we could also rename it to `asOfTime()`? -- Given
that
we
strongly type with `Instant` I am not worried about semantic
ambiguity.



20) About throwing a NPE when time bounds are `null` -- why?
(For
the
key it makes sense as is mandatory to have a key.) Could
we not
interpret `null` as "no bound". We did KIP-941 to add `null`
for
open-ended `RangeQueries`, so I am wondering if we should
just stick
to
the same semantics?

Cf




https://cwiki.apache.org/confluence/display/KAFKA/KIP-941%3A+Range+queries+to+accept+null+lower+and+upper+bounds



30) About the class naming. That's always tricky, and I am
not
married
to my proposal. I agree with Bruno that the other suggested
names
are
not really better. -- The underlying idea was, to get some
"consistent"
naming across the board.

Existing `KeyQuery`
New `VersionedKeyQuery` (KIP-960; we add a prefix)
New `MultiVersionKeyQuery` (this KIP; extend the prefix
with a
pre-prefix)

Existing `RangeQuery`
New `MultiVersionRangeQuery` (KIP-969; add same prefix as
above)



40) I am fine with not adding `range(from, to)` -- it was
just an
idea.





Some more follow up question:

50) You propose to add a new constructor and getter to
`VersionedRecord`
-- I am wondering if this implies that `validTo` is optional
because
the
existing constructor is not deprecated? -- Also, what
happens
if
`validTo` is not set and `valueTo()` is called? Or do we
intent to
make
`validTo` mandatory?

Maybe this question can only be answered when working on the
code,
but I
am wondering if we should make `validTo` mandatory or not...
And
what
the "blast radius" of changing `VersionedRecord` will be in
general.
Do
you have already some POC PR that we could look at to get
some
signals
about this?



60) The new query class is defined to return
`ValueIterator<VersionedRecord<V>>` -- while I like the idea
to add
`ValueIterator<V>` in a generic way on the one hand, I am
wondering if
it might be better to change it, and enforce its usage (ie,
return
type)
of `VersionedRecord` to improve type safety (type erasure is
often a
pain, and we could mitigate it this way).

Btw: We actually do a similar thing for `KeyValueIterator`.

Ie,

public interface ValueIterator<V> extends
Iterator<ValueAndTimestamp<V>>

and

ValueAndTimestamp<V> peek();

This would imply that the return type of the new query is
`ValueIterator<V>` on the interface what seems simpler and
more
elegant?

If we go with the change, I am also wondering if we need to
find a
better name for the new iterator class? Maybe
`VersionIterator` or
something like this?

Of course it might limit the use of `ValueIterator` for
other
value
types -- not sure if this a limitation that is prohibitive?
My gut
feeling is, that is should not be too limiting.




70) Do we really need the change in `VersionedKeyValueStore`
and
add a
new method? In the end, the idea of IQv2 is to avoid exactly
this...
It
was the main issue for IQv1, that the base interface of the
store
needed
an update and thus all classed implementing the base
interface,
making
it very cumbersome to add new query types. -- Of course, we
need
this
new method on the actually implementation (as private
method)
that can
be called from `query()` method, but adding it to the
interface
seems
to
defeat the purpose of IQv2.

Note, for existing IQv2 queries types that go against others
stores,
the
public methods already existed when IQv2 was introduces, and
thus
the
implementation of these query types just pragmatically
re-used
existing
methods -- but it does not imply that new public method
should be
added.




-Matthias


On 10/11/23 5:11 AM, Bruno Cadonna wrote:
Thanks for the updates, Alieh!

The example in the KIP uses the allVersions() method
which we
agreed
to
remove.

Regarding your questions:
1. asOf vs. until: I am fine with both but slightly prefer
until.
2. What about KeyMultiVersionsQuery, KeyVersionsQuery
(KIP-960
would
then be KeyVersionQuery). However, I am also fine with
MultiVersionedKeyQuery since none of the names sounds
better
or
worse
to
me.
3. I agree with you not to introduce the method with the
two
bounds
to
keep things simple.
4. Forget about fromTime() an asOfTime(), from() and
asOf() is
fine.
5. The main purpose is to show how to use the API. Maybe
make an
example
with just the key to distinguish this query from the single
value
query
of KIP-960 and then one with a key and a time range. When
you
iterate
over the results you could also call validTo(). Maybe add
some
actual
records in the comments to show what the result might look
like.

Regarding the test plan, I hope you also plan to add unit
tests in
all
of your KIPs. Maybe you could also explain why system tests
are not
needed here.

Best,
Bruno

On 10/10/23 5:36 PM, Alieh Saeedi wrote:
Thank you all for the very exact and constructive
comments. I
really
enjoyed reading your ideas and all the important points
you
made me
aware
of. I updated KIP-968 as follows:

         1. If the key or time bounds are null, the method
returns
NPE.
         2. The "valid" word: I removed the sentence "all
the
records
that are
         valid..." and replaced it with an exact
explanation.
More
over, I
explained
         it with an example in the KIP but not in the
javadocs. Do I
need
to add the
         example to the javadocs as well?
         3. Since I followed Bruno's suggestion and
removed the
allVersions()
         method, the problem of meaningless combinations is
solved, and
I
do not
         need any IllegalArgumentException or something
like
that.
Therefore, the
         change is that if no time bound is specified, the
query
returns
the records
         with the specified key for all timestamps (all
versions).
         4. As Victoria suggested, adding a method to the
*VersionedKeyValueStore
         *interface is essential. So I did that. I had this
method
only
in the
         RocksDBVersionedStore class, which was not enough.
         5. I added the *validTo* field to the
VersionedRecord
class to
be
able
         to represent the tombstones. As you suggested, we
postpone
solving
the
         problem of retrieving consecutive tombstones for
later.
         6. I added the "Test Plan" section to all KIPs.
I hope
what I
wrote is
         convincing.
         7. I added the *withAscendingTimestamp()* method
to
provide
more
code readability
         for the user.
         8. I removed the evil word "get" from all getter
methods.

There have also been some more suggestions which I am
still
not
convinced
or clear about them:

         1. Regarding asOf vs until: reading all
comments, my
conclusion
was that
         I keep it as "asOf" (following Walker's idea as
the
native
speaker
as well
         as Bruno's suggestion to be consistent with
single-key_single_ts
queries).
         But I do not have a personal preference. If you
insist on
"until",
I change
         it.
         2. Bruno suggested renaming the class
"MultiVersionedKeyQuery"
to sth
         else. We already had a long discussion about the
name with
Matthias. I am
         open to renaming it to something else, but do you
have any
ideas?
         3. Matthias suggested having a method with two
input
parameters
that
         enables the user to specify both time bounds in
the
same
method.
Isn't it
         introducing redundancy? It is somehow
disrespectful to
the idea
of
having
         composable methods.
         4. Bruno suggested renaming the methods "asOf" and
"from" to
"asOfTime"
         and "fromTime". If I do that, then it is not
consistent with
KIP-960.
         Moreover, the input parameter is clearly a
timestamp, which
explains
         enough. What do you think about that?
         5. I was asked to add more examples to the example
section. My
question
         is, what is the main purpose of that? If I know it
clearly,
then
I
can add
         what you mean.



Cheers,
Alieh

On Tue, Oct 10, 2023 at 1:13 AM Matthias J. Sax
<mj...@apache.org>
wrote:

Bruno and I had some background conversation about the
`get`
prefix
question including a few other committers.

The official policy was never changed, and we should not
add the
`get`-prefix. It's a slip on our side in previous KIPs to
add the
`get`-prefix and we should actually clean it up doing a
follow up
KIP.


-Matthias


On 10/5/23 5:26 AM, Bruno Cadonna wrote:
Hi Matthias,

Given all the IQv2 KIPs that use getX and given recent
PRs
(internal
interfaces mainly) that got merged, I was under the
impression
that we
moved away from the strict no-getX policy.

I do not think it was an accident using getX in the IQv2
KIPs
since
somebody would have brought it up, otherwise.

I am fine with both types of getters.

If we think, we need to discuss this in a broader
context, let's
start a
separate thread.


Best,
Bruno





On 10/5/23 7:44 AM, Matthias J. Sax wrote:
I agree to (almost) everything what Bruno said.


In general, we tend to move away from using getters
without
"get",
recently. So I would keep the "get".

This is new to me? Can you elaborate on this point? Why
do you
think
that's the case?

I actually did realize (after Walker mentioned it) that
existing
query
types use `get` prefix, but to me it seems that it
was by
accident and
we should consider correcting it? Thus, I would
actually
prefer
to not
add the `get` prefix for new methods query types.

IMHO, we should do a follow up KIP to deprecate all
methods
with
`get`
prefix and replace them with new ones without `get` --
it's of
course
always kinda "unnecessary" noise, but if we don't do
it,
we
might
get
into more and more inconsistent naming what would
result
in a
"bad"
API.

If we indeed want to change the convention and use the
`get`
prefix, I
would strongly advocate to bit the bullet and do KIP to
pro-actively
add the `get` "everywhere" it's missing... But
overall, it
seems
to be
a much broader decision, and we should get buy in from
many
committers
about it -- as long as there is no broad consensus to
add `get`
everywhere, I would strongly prefer not to diverge from
the
current
agreement to omit `get`.



-Matthias




On 10/4/23 2:36 AM, Bruno Cadonna wrote:
Hi,

Regarding tombstones:
As far as I understand, we need to add either a
validTo
field to
VersionedRecord or we need to return tombstones,
otherwise the
result
is not complete, because users could never know a
record was
deleted
at some point before the second non-null value was
put.
I like more adding the validTo field since it makes
the
result
more
concise and easier interpretable.

Extending on Victoria's example, with the following
puts

put(k, v1, time=0)
put(k, null, time=5)
put(k, null, time=10)
put(k, null, time=15)
put(k, v2, time=20)

the result with tombstones would be

value, timestamp
(v1, 0)
(null, 5)
(null, 10)
(null, 15)
(v2, 20)

instead of

value, timestamp, validTo
(v1, 0, 5)
(v2, 20, null)

The benefit of conciseness would already apply to one
single
tombstone.

On the other hand, why would somebody write
consecutive
tombstones
into a versioned state store? I guess if somebody does
that on
purpose, then there should be a way to retrieve each
of
those
tombstones, right?
So maybe we need both -- validTo field and the
option to
return
tombstones. The latter might be moved to a future
KIP in
case we
see
the need.


Regarding .within(fromTs, toTs):
I would keep it simple with .from() and
.asOfTimestamp() (or
.until()). If we go with .within(), I would opt for
.withinTimeRange(fromTs, toTs), because the query
becomes more
readable:

MultiVersionedKeyQuery
         .withKey(1)

.withinTimeRange(Instant.parse(2023-08-03T10:37:30.00Z),
Instant.parse(2023-08-04T10:37:30.00Z))

If we stay with .from() and .until(), we should
consider
.fromTime()
and .untilTime() (or .toTime()):

MultiVersionedKeyQuery
        .withKey(1)

.fromTime(Instant.parse(2023-08-03T10:37:30.00Z))

.untilTime(Instant.parse(2023-08-04T10:37:30.00Z))



Regarding asOf vs. until:
I think asOf() is more used in point in time queries
as
Walker
mentioned where this KIP specifies a time range. IMO
asOf()
fits
very
well with KIP-960 where one version is queried, but
here
I
think
.until() fits better. That might just be a matter of
taste and
in the
end I am fine with both as long as it is well
documented.


Regarding getters without "get":
In the other IQv2 classes we used getters with
"get". In
general, we
tend to move away from using getters without "get",
recently. So
I
would keep the "get".


Best,
Bruno

On 10/3/23 7:49 PM, Walker Carlson wrote:
Hey Alieh thanks for the KIP,

Weighing in on the AsOf vs Until debate I think
either
is
fine
from a
natural language perspective. Personally AsOf makes
more
sense
to me
where
until gives me the idea that the query is making a
change.
It's
totally a
connotative difference and not that important. I
think
as
of is
pretty
frequently used in point of time queries.

Also for these methods it makes sense to drop the
"get" We
don't
normally use that in getters

          * The key that was specified for this query.
          */
         public K getKey();

         /**
          * The starting time point of the query, if
specified
          */
         public Optional<Instant> getFromTimestamp();

         /**
          * The ending time point of the query, if
specified
          */
         public Optional<Instant> getAsOfTimestamp();

Other than that I didn't have too much to add.
Overall
I like
the
direction
of the KIP and think the funcatinlyt is all there!
best,
Walker



On Mon, Oct 2, 2023 at 10:46 PM Matthias J. Sax <
mj...@apache.org>
wrote:

Thanks for the updated KIP. Overall I like it.

Victoria raises a very good point, and I personally
tend to
prefer (I
believe so does Victoria, but it's not totally clear
from
her
email) if
a range query would not return any tombstones, ie,
only two
records
in
Victoria's example. Thus, it seems best to include a
`validTo`
ts-field
to `VersionedRecord` -- otherwise, the retrieved
result
cannot
be
interpreted correctly.

Not sure what others think about it.

I would also be open to actually add a
`includeDeletes()`
(or
`includeTombstones()`) method/flag (disabled by
default) to
allow
users
to get all tombstone: this would only be helpful if
there
are
two
consecutive tombstone though (if I got it right), so
not
sure
if we
want
to add it or not -- it seems also possible to add it
later if
there
is
user demand for it, so it might be a premature
addition as
this
point?


Nit:

the public interface ValueIterator is used

"is used" -> "is added" (otherwise it sounds like
as if
`ValueIterator`
exist already)



Should we also add a `.within(fromTs, toTs)` (or
maybe some
better
name?) to allow specifying both bounds at once? The
existing
`RangeQuery` does the same for specifying the
key-range, so
might be
good to add for time-range too?



-Matthias


On 9/6/23 5:01 AM, Bruno Cadonna wrote:
In my last e-mail I missed to finish a sentence.

"I think from a KIP"

should be

"I think the KIP looks good!"


On 9/6/23 1:59 PM, Bruno Cadonna wrote:
Hi Alieh,

Thanks for the KIP!

I think from a KIP

1.
I propose to throw an IllegalArgumentException
or an
IllegalStateException for meaningless
combinations.
In any
case,
the
KIP should specify what exception is thrown.

2.
Why does not specifying a range return the latest
version? I
would
expect that it returns all versions since an empty
lower or
upper
limit is interpreted as no limit.

3.
I second Matthias comment about replacing "asOf"
with
"until" or
"to".

4.
Do we need "allVersions()"? As I said above I
would
return
all
versions if no limits are specified. I think if we
get rid
of
allVersions() there might not be any meaningless
combinations
anymore.
If a user applies twice the same limit like for
example
MultiVersionedKeyQuery.with(key).from(t1).from(t2)
the
last
one
wins.

5.
Could you add some more examples with time ranges
to the
example
section?

6.
The KIP misses the test plan section.

7.
I propose to rename the class to
"MultiVersionKeyQuery"
since we
are
querying multiple versions of the same key.

8.
Could you also add withAscendingTimestamps()? IMO
it gives
users
the
possibility to make their code more readable
instead of
only
relying
on the default.

Best,
Bruno


On 8/17/23 4:13 AM, Matthias J. Sax wrote:
Thanks for splitting this part into a separate
KIP!

For `withKey()` we should be explicit that `null`
is not
allowed.

(Looking into existing `KeyQuery` it seems the
JavaDocs
don't
cover
this either -- would you like to do a tiny
cleanup
PR for
this, or
fix on-the-side in one of your PRs?)



The key query returns all the records that are
valid in
the
time
range starting from the timestamp {@code
fromTimestamp}.

In the JavaDocs you use the phrase `are valid` --
I think
we
need to
explain what "valid" means? It might even be
worth
to add
some
examples. It's annoying, but being precise if
kinda
important.

With regard to KIP-962, should we allow `null`
for
time
bounds ?
The
JavaDocs should also be explicit if `null` is
allowed or
not and
what
the semantics are if allowed.



You are using `asOf()` however, because we are
doing
time-range
queries, to me using `until()` to describe the
upper
bound
would
sound better (I am not a native speaker though,
so
maybe I
am
off?)


The key query returns all the records that have
timestamp
<=
{@code
asOfTimestamp}.

This is only correct if not lower-bound is set,
right?


In your reply to KIP-960 you mentioned:

the meaningless combinations are prevented by
throwing
exceptions.

We should add corresponding JavaDocs like:

           @throws IllegalArgumentException if
{@code
fromTimestamp} is
equal or
                                            larger
than
{@code
untilTimestamp}

Or something similar.


With regard to KIP-960: if we need to introduce a
`VersionedKeyQuery`
class for single-key-single-ts lookup, would we
need to
find
a new
name for the query class of this KIP, given
that the
return
type
is
different?


-Matthias



On 8/16/23 10:57 AM, Alieh Saeedi wrote:
Hi all,

I splitted KIP-960
<





https://cwiki.apache.org/confluence/display/KAFKA/KIP-960%3A+Support+single-key_single-timestamp+interactive+queries+%28IQv2%29+for+versioned+state+stores

into three separate KIPs. Therefore, please
continue
discussions
about single-key, multi-timestamp interactive
queries
here. You
can
see all
the addressed reviews on the following page.
Thanks in
advance.

KIP-968: Support single-key_multi-timestamp
interactive
queries
(IQv2) for
versioned state stores
<





https://cwiki.apache.org/confluence/display/KAFKA/KIP-968%3A+Support+single-key_multi-timestamp+interactive+queries+%28IQv2%29+for+versioned+state+stores


I look forward to your feedback!

Cheers,
Alieh















Reply via email to