Skip to content

Commit

Permalink
#755 Add async fetch to DataProvider, revise low-water mark to 33%
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotteveel committed Jul 5, 2023
1 parent c7e5ac7 commit 656a8a6
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 6 deletions.
3 changes: 2 additions & 1 deletion devdoc/jdp/jdp-2023-12-async-fetching-result-set-rows.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
== Status

* Published: 2023-07-04
* Updated: 2023-07-05
* Implemented in: Jaybird 6

== Type
Expand Down Expand Up @@ -65,7 +66,7 @@ Modification of `FBFetcher` only seems to make sense for `FBStatementFetcher`, b
It may also make sense in `FBCachedFetcher`, but this might be harder to do correctly.

The fetcher implementation will track the "`high-water mark`" of rows (the maximum number of rows returned by a single fetch), and evaluate when to async fetch (on the "`low-water mark`").
This JDP does not specify exactly _when_ to fetch, but as a starting point, we'll perform an async fetch at 30% of the "`high-water mark`" or 10 rows, whichever is higher.
This JDP does not specify exactly _when_ to fetch, but as a starting point, we'll perform an async fetch at ~33% of the "`high-water mark`" or 10 rows, whichever is higher.
If the "`high-water mark`" is less than 15 rows, we'll not perform an async fetch.
This logic may be modified during evaluation and further testing, and should be considered an implementation detail, not a specification.

Expand Down
4 changes: 2 additions & 2 deletions src/docs/asciidoc/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ See also https://github.com/FirebirdSQL/jaybird/blob/master/devdoc/jdp/jdp-2023-
[#async-fetch]
=== Asynchronous fetching

For pure Java connections, forward-only result sets now support asynchronous fetches.
For pure Java connections, forward-only result sets now perform asynchronous fetches.
Asynchronous fetches are implemented for protocol implementation version 11 and higher (i.e. Firebird 2.1 or higher), but are formally only supported for protocol version 13 and higher (i.e. Firebird 3.0 or higher).

In normal usage of a result set, the first fetch will be a normal synchronous fetch.
Expand All @@ -697,7 +697,7 @@ In Jaybird 6.0.0, the condition to perform an asynchronous fetch is:

* Size of the row buffer is equal to the "`low-water mark`".
+
This "`low-water mark`" is calculated as 30% of the maximum number of rows returned by previous fetches, with a minimum of 10 rows.
This "`low-water mark`" is calculated as ~33% of the maximum number of rows returned by previous fetches, with a minimum of 10 rows.
+
As a consequence of this condition, reducing the fetch size equal to or less than the "`low-water mark`" may cause asynchronous fetches to stop being triggered, as the buffer size may remain smaller than the "`low-water mark`" (this effect may be reduced if there is other activity on the connection after the asynchronous fetch was performed).
In that case a synchronous fetch is triggered when the buffer is empty.
Expand Down
19 changes: 18 additions & 1 deletion src/main/org/firebirdsql/jaybird/xca/FBManagedConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -798,10 +798,14 @@ public boolean isLockedByCurrentThread() {
}

private static final class DataProvider implements StatementListener {

private static final int NO_ASYNC_FETCH = -1;

private final Deque<RowValue> rows = new ArrayDeque<>();
private final FbStatement statementHandle;
private RowValue currentRow = null;
private RowValue currentRow;
private boolean moreRows = true;
private int fetchAsyncAt = NO_ASYNC_FETCH;

private DataProvider(FbStatement statementHandle) {
this.statementHandle = statementHandle;
Expand All @@ -810,6 +814,8 @@ private DataProvider(FbStatement statementHandle) {
boolean hasNext() throws SQLException {
if (rows.isEmpty() && moreRows) {
fetch();
} else if (rows.size() == fetchAsyncAt && moreRows) {
fetchAsync();
}
return !rows.isEmpty();
}
Expand All @@ -827,6 +833,10 @@ private void fetch() throws SQLException {
statementHandle.fetchRows(Integer.MAX_VALUE);
}

private void fetchAsync() throws SQLException {
statementHandle.asyncFetchRows(Integer.MAX_VALUE);
}

@Override
public void receivedRow(FbStatement sender, RowValue rowValue) {
rows.add(rowValue);
Expand All @@ -837,6 +847,13 @@ public void afterLast(FbStatement sender) {
moreRows = false;
}

@Override
public void fetchComplete(FbStatement sender, FetchDirection fetchDirection, int rows) {
if (fetchAsyncAt * 3 < rows) {
fetchAsyncAt = rows >= 15 ? Math.min(rows / 3, 200) : NO_ASYNC_FETCH;
}
}

FieldDataProvider asFieldDataProvider(int fieldPos) {
return new FieldDataProvider() {
@Override
Expand Down
4 changes: 2 additions & 2 deletions src/main/org/firebirdsql/jdbc/FBStatementFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class FBStatementFetcher implements FBFetcher {

private static final int NO_ASYNC_FETCH = -1;
private static final int MINIMUM_ASYNC_FETCH_ROW_COUNT = 10;
private static final float ASYNC_FETCH_PERCENTAGE = 0.3f;
private static final int ASYNC_FETCH_FACTOR = 3;

private boolean closed;
private boolean wasFetched;
Expand Down Expand Up @@ -353,7 +353,7 @@ public void fetchComplete(FbStatement sender, FetchDirection fetchDirection, int
maxActualFetchSize = rows;
if (rows >= MINIMUM_ASYNC_FETCH_ROW_COUNT * 3 / 2 ) {
asyncFetchOnRemaining =
Math.max((int) (rows * ASYNC_FETCH_PERCENTAGE), MINIMUM_ASYNC_FETCH_ROW_COUNT);
Math.max(rows / ASYNC_FETCH_FACTOR, MINIMUM_ASYNC_FETCH_ROW_COUNT);
}
}
}
Expand Down

0 comments on commit 656a8a6

Please sign in to comment.