Skip to content

Commit eff1c0b

Browse files
committed
#841 Improve blob performance by combining open and first fetch - backport to Jaybird 5
1 parent e8cc6d5 commit eff1c0b

File tree

18 files changed

+704
-135
lines changed

18 files changed

+704
-135
lines changed

src/docs/asciidoc/release_notes.adoc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ Changes per Jaybird 5 release.
2929
See also <<whats-new-in-jaybird-5>>.
3030
For known issues, consult <<known-issues>>.
3131

32+
[#jaybird-5-0-7-changelog]
33+
=== Jaybird 5.0.7
34+
35+
The following has been changed or fixed since Jaybird 5.0.6:
36+
37+
* Improvement: backported deferred blob open optimization from Jaybird 7 (https://github.com/FirebirdSQL/jaybird/issues/841[#841])
38+
+
39+
See also <<blob-performance-defer-open>>.
40+
3241
[#jaybird-5-0-6-changelog]
3342
=== Jaybird 5.0.6
3443

@@ -752,6 +761,24 @@ This is an experimental feature.
752761
Its API may change in point releases, or it may be removed or replaced entirely in a future major release.
753762
====
754763

764+
[#blob-performance]
765+
=== Blob performance improvements
766+
767+
[#blob-performance-defer-open]
768+
==== Deferred blob open
769+
770+
Added in: Jaybird 5.0.7, backported from Jaybird 7
771+
772+
In the pure Java implementation, performance of reading and writing blobs was improved by deferring the server-side opening or creating of a blob until an actual server-side operation (putting or getting a segment, or getting blob info).
773+
The open or create blob request is pipelined with the subsequent operation, avoiding a round trip to the server.
774+
This is especially noticeable in connections with high latency.
775+
776+
Artificial testing on local WiFi with small blobs shows around 85% increase in throughput (comparing a 6.0.1-SNAPSHOT against 6.0.0).
777+
778+
This optimization is available for Firebird 2.1 and higher, but formally only supported for Firebird 2.5 and higher.
779+
780+
For native connections, a similar optimization -- but only for reading blobs -- is available when using a Firebird 5.0.2 or higher fbclient, independent of the Jaybird version.
781+
755782
[#potentially-breaking-changes]
756783
=== Potentially breaking changes
757784

src/jna-client/org/firebirdsql/gds/ng/jna/JnaBlob.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public void open() throws SQLException {
134134
checkDatabaseAttached();
135135
checkTransactionActive();
136136
checkBlobClosed();
137+
clearDeferredException();
137138

138139
final JnaDatabase db = getDatabase();
139140
if (isOutput()) {
@@ -144,11 +145,12 @@ public void open() throws SQLException {
144145
getJnaHandle(), blobId, (short) bpb.length, bpb);
145146
}
146147
processStatusVector();
147-
setOpen(true);
148+
setState(BlobState.OPEN);
148149
resetEof();
150+
throwAndClearDeferredException();
149151
}
150152
} catch (SQLException e) {
151-
exceptionListenerDispatcher.errorOccurred(e);
153+
errorOccurred(e);
152154
throw e;
153155
}
154156
}
@@ -185,13 +187,14 @@ public byte[] getSegment(int sizeRequested) throws SQLException {
185187
} else if (!(status == 0 || status == ISCConstants.isc_segment)) {
186188
processStatusVector();
187189
}
190+
throwAndClearDeferredException();
188191
}
189192
final int actualLengthInt = ((int) actualLength.getValue()) & 0xFFFF;
190193
final byte[] segment = new byte[actualLengthInt];
191194
responseBuffer.get(segment);
192195
return segment;
193196
} catch (SQLException e) {
194-
exceptionListenerDispatcher.errorOccurred(e);
197+
errorOccurred(e);
195198
throw e;
196199
}
197200
}
@@ -213,9 +216,10 @@ public void putSegment(byte[] segment) throws SQLException {
213216

214217
clientLibrary.isc_put_segment(statusVector, getJnaHandle(), (short) segment.length, segment);
215218
processStatusVector();
219+
throwAndClearDeferredException();
216220
}
217221
} catch (SQLException e) {
218-
exceptionListenerDispatcher.errorOccurred(e);
222+
errorOccurred(e);
219223
throw e;
220224
}
221225
}
@@ -225,15 +229,17 @@ public void seek(int offset, SeekMode seekMode) throws SQLException {
225229
try (LockCloseable ignored = withLock()) {
226230
checkDatabaseAttached();
227231
checkTransactionActive();
232+
checkBlobOpen();
228233

229234
// result is the current position in the blob (see .NET provider source)
230235
// We ignore the result TODO check if useful; not used in wire protocol either
231236
IntByReference result = new IntByReference();
232237
clientLibrary.isc_seek_blob(statusVector, getJnaHandle(), (short) seekMode.getSeekModeId(), offset,
233238
result);
234239
processStatusVector();
240+
throwAndClearDeferredException();
235241
} catch (SQLException e) {
236-
exceptionListenerDispatcher.errorOccurred(e);
242+
errorOccurred(e);
237243
throw e;
238244
}
239245
}
@@ -245,17 +251,19 @@ public byte[] getBlobInfo(byte[] requestItems, int bufferLength) throws SQLExcep
245251
try (LockCloseable ignored = withLock()) {
246252
responseBuffer = getByteBuffer(bufferLength);
247253
checkDatabaseAttached();
254+
checkBlobOpen();
248255
clientLibrary.isc_blob_info(statusVector, getJnaHandle(),
249256
(short) requestItems.length, requestItems,
250257
(short) bufferLength, responseBuffer);
251258
processStatusVector();
259+
throwAndClearDeferredException();
252260
}
253261

254262
byte[] responseArr = new byte[bufferLength];
255263
responseBuffer.get(responseArr);
256264
return responseArr;
257265
} catch (SQLException e) {
258-
exceptionListenerDispatcher.errorOccurred(e);
266+
errorOccurred(e);
259267
throw e;
260268
}
261269
}

src/main/org/firebirdsql/gds/impl/wire/WireProtocolConstants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
*/
3232
public interface WireProtocolConstants {
3333

34+
/**
35+
* Object handle id that can be used to refer to a just created server-side object (for protocol versions supporting
36+
* {@link #ptype_lazy_send}).
37+
* <p>
38+
* This can be used to pipeline the creation of an object with the subsequent use of that object without waiting on
39+
* the server response with the actual handle id.
40+
* </p>
41+
*/
3442
int INVALID_OBJECT = 0xFFFF;
3543

3644
/* Operation (packet) types */

0 commit comments

Comments
 (0)