Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Window Operators Create Dangling Windows #1033

Closed
nebhale opened this issue Jan 22, 2018 · 4 comments
Closed

Window Operators Create Dangling Windows #1033

nebhale opened this issue Jan 22, 2018 · 4 comments
Labels
type/enhancement A general enhancement
Milestone

Comments

@nebhale
Copy link
Member

nebhale commented Jan 22, 2018

Currently, when a Flux ends with a value that would close a window, another window is immediately opened, and closed without ever having an element provided. For example, the two following tests fail:

@Test
public void windowWhile() {
    Flux.just("ALPHA", "#", "ALPHA", "#")
        .windowWhile(s -> !"#".equals(s))
        .as(StepVerifier::create)
        .expectNextCount(2)
        .verifyComplete();
}

@Test
public void windowUntil() {
    Flux.just("ALPHA", "#", "ALPHA", "#")
        .windowUntil("#"::equals)
        .as(StepVerifier::create)
        .expectNextCount(2)
        .verifyComplete();
}

I believe that windows should only be opened on the first element of a new window, which would cause these two tests to pass. This would have the added benefit of matching how the buffer operators behave.

@simonbasle
Copy link
Member

This was a conscious decision when implementing the operator: separate housekeeping of the window creation + emission was proving difficult...

Simplifying this resulted in empty windows being emitted at the end of the sequence if it terminated with a separator, and in the case of windowWhile also if multiple subsequent separators were found. This was expected not to be much of a problem, since eg. a typical flatMap on a window will basically "ignore" an empty window.

Note that there can't be perfect alignment with bufferUntil/While: it is only emitted when the buffer is closed, whereas with a window you want to be able to process elements in the window as they come in.

Is there a strong argument for trying to get rid of these empty windows? Otherwise, might it be better covered by some added documentation (I tried to mention the empty window cases in each javadoc already)?

@nebhale
Copy link
Member Author

nebhale commented Jan 23, 2018

I believe the emission of empty windows is a problem in that any expectation of the number of windows is now off-by-one. Imagine executing an SQL query select * from table_1; select * from table_2 that is windowed by the results of the two query statements. Any attempt to reconcile (especially in testing) the results of those two queries will fail as there's a "result" at the end that does not map to any query. It's even more pronounced if you attempted to execute a single query select * from table_1, then reduce it via .window(...).single(...) to ensure that only a single result came back.

RxJava's windowing operators don't have this tail window, and an example algorithm (functional, albeit non-optimal because of #1034) can be found in my r2dbc project. It is roughly:

if (isBoundary) {
  if (!inWindow) {
    open new window
  }
  close window
} else {
  if (!inWindow) {
    open new window
  }
  add to window
}

@Kindrat
Copy link

Kindrat commented Jan 23, 2018

Agree with @nebhale - you can't just cover with documentation some unexpected or wrong behavior b/c t is still risky for your users... An empty window is not exactly what you want to get during processing.

@simonbasle
Copy link
Member

@Kindrat in my opinion there is nothing inherently wrong with an empty window. It was even requested that the windowing-by-time operator emits empty windows so that it can be used as a heartbeat IIRC. You're unexpected behavior might be someone else's absolutely non-negotiable feature; but eh...

@nebhale I don't expect it to be as simple as your combination of handle and processors, being a full blown operator, but we can give it a try, at least with the tail window.

@simonbasle simonbasle added this to the 3.1.4.RELEASE milestone Jan 25, 2018
@smaldini smaldini added the type/enhancement A general enhancement label Feb 1, 2018
simonbasle added a commit that referenced this issue Mar 8, 2018
Offer and drain in all situations, even those where no emission.

This results in subsequent separators or sequences that start with a
separator to emit an empty window, but avoids a remainder window on
source completion, when said window is empty.
smaldini pushed a commit that referenced this issue May 6, 2018
Offer and drain in all situations, even those where no emission.

This results in subsequent separators or sequences that start with a
separator to emit an empty window, but avoids a remainder window on
source completion, when said window is empty.
smaldini pushed a commit that referenced this issue May 8, 2018
Offer and drain in all situations, even those where no emission.

This results in subsequent separators or sequences that start with a
separator to emit an empty window, but avoids a remainder window on
source completion, when said window is empty.
@simonbasle simonbasle modified the milestones: 3.2.0.RELEASE, 3.2.0.M2 May 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants