Skip to content

Commit bb2ba41

Browse files
authored
Wire protocol improvement: prefetch blob info and some data when open blob (#8307)
* Group op_open_blob2, op_info_blob and op_get_segment into single physical packet. It allows to prefetch and cache blob info and some blob data in single network roundtrip. Return cached blob info, if present, without remote access. * No need to reserve space for segment length. Fixed typo. Thanks to @dyemanov for comments. * Add 'const' for parameter declaration, as @AlexPeshkoff suggested
1 parent 1eacdd4 commit bb2ba41

File tree

4 files changed

+264
-4
lines changed

4 files changed

+264
-4
lines changed

src/common/utils_proto.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
#define INCLUDE_UTILS_PROTO_H
3131

3232
#include <string.h>
33+
#include <type_traits>
34+
3335
#include "../common/classes/fb_string.h"
3436
#include "../common/classes/array.h"
3537
#include "iberror.h"
3638
#include "firebird/Interface.h"
39+
#include "memory_routines.h"
3740

3841
#ifdef SFIO
3942
#include <stdio.h>
@@ -263,6 +266,46 @@ namespace fb_utils
263266
// Frequently used actions with clumplets
264267
bool isBpbSegmented(unsigned parLength, const unsigned char* par);
265268

269+
270+
// Put integer value into info buffer
271+
template<typename T>
272+
inline unsigned char* putInfoItemInt(const unsigned char item, T value,
273+
unsigned char* ptr, const unsigned char* end)
274+
{
275+
static_assert(std::is_integral_v<T>, "Integral type expected");
276+
277+
constexpr auto len = sizeof(T);
278+
static_assert(len == 1 || len == 2 || len == 4 || len == 8, "unknown data type");
279+
280+
if (ptr + len + 1 + 2 > end)
281+
{
282+
if (ptr < end)
283+
{
284+
*ptr++ = isc_info_truncated;
285+
if (ptr < end)
286+
*ptr++ = isc_info_end;
287+
}
288+
return nullptr;
289+
}
290+
291+
*ptr++ = item;
292+
*ptr++ = len;
293+
*ptr++ = 0;
294+
295+
if (len == 8)
296+
put_vax_int64(ptr, value);
297+
else if (len == 4)
298+
put_vax_long(ptr, value);
299+
else if (len == 2)
300+
put_vax_short(ptr, value);
301+
else if (len == 1)
302+
*ptr = value;
303+
304+
ptr += len;
305+
return ptr;
306+
}
307+
308+
266309
// RAII to call fb_shutdown() in utilities
267310
class FbShutdown
268311
{

src/remote/client/interface.cpp

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,10 @@ void Blob::getInfo(CheckStatusWrapper* status,
13031303

13041304
Rdb* rdb = blob->rbl_rdb;
13051305
CHECK_HANDLE(rdb, isc_bad_db_handle);
1306+
1307+
if (blob->rbl_info.getLocalInfo(itemsLength, items, bufferLength, buffer))
1308+
return;
1309+
13061310
rem_port* port = rdb->rdb_port;
13071311
RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
13081312

@@ -5610,7 +5614,79 @@ IBlob* Attachment::openBlob(CheckStatusWrapper* status, ITransaction* apiTra, IS
56105614
// would try to write to the application's provided R/O buffer.
56115615
p_blob->p_blob_bpb.cstr_address = bpb;
56125616

5613-
send_and_receive(status, rdb, packet);
5617+
UCHAR infoBuffer[128];
5618+
5619+
if (port->port_flags & PORT_lazy)
5620+
{
5621+
send_partial_packet(port, packet);
5622+
5623+
// prefetch blob info
5624+
const UCHAR items[] = {
5625+
isc_info_blob_num_segments,
5626+
isc_info_blob_max_segment,
5627+
isc_info_blob_total_length,
5628+
isc_info_blob_type,
5629+
isc_info_end
5630+
};
5631+
5632+
packet->p_operation = op_info_blob;
5633+
P_INFO* information = &packet->p_info;
5634+
information->p_info_object = INVALID_OBJECT;
5635+
information->p_info_incarnation = 0;
5636+
information->p_info_items.cstr_length = sizeof(items);
5637+
information->p_info_items.cstr_address = items;
5638+
information->p_info_buffer_length = sizeof(infoBuffer);
5639+
5640+
send_partial_packet(port, packet);
5641+
5642+
// prefetch some data
5643+
packet->p_operation = op_get_segment;
5644+
P_SGMT* segment = &packet->p_sgmt;
5645+
segment->p_sgmt_length = BLOB_LENGTH;
5646+
segment->p_sgmt_blob = INVALID_OBJECT;
5647+
segment->p_sgmt_segment.cstr_length = 0;
5648+
5649+
send_packet(port, packet);
5650+
5651+
try
5652+
{
5653+
receive_response(status, rdb, packet);
5654+
}
5655+
catch (const Exception& ex)
5656+
{
5657+
// re-throw network error immediately, for other errors receive two more packets first
5658+
if (port->port_state != rem_port::PENDING)
5659+
throw;
5660+
5661+
FbLocalStatus local;
5662+
ex.stuffException(&local);
5663+
5664+
auto errs = local->getErrors();
5665+
5666+
if (fb_utils::containsErrorCode(errs, isc_network_error) ||
5667+
fb_utils::containsErrorCode(errs, isc_net_read_err) ||
5668+
port->port_state != rem_port::PENDING)
5669+
{
5670+
throw;
5671+
}
5672+
5673+
for (int i = 0; i < 2; i++)
5674+
{
5675+
try
5676+
{
5677+
UseStandardBuffer temp(packet->p_resp.p_resp_data);
5678+
receive_response(status, rdb, packet);
5679+
}
5680+
catch (const Exception&) {}
5681+
}
5682+
5683+
throw;
5684+
}
5685+
}
5686+
else
5687+
{
5688+
send_and_receive(status, rdb, packet);
5689+
}
56145690

56155691
// CVC: It's not evident to me why these two lines that I've copied
56165692
// here as comments are only found in create_blob calls.
@@ -5626,9 +5702,46 @@ IBlob* Attachment::openBlob(CheckStatusWrapper* status, ITransaction* apiTra, IS
56265702
blob->rbl_next = transaction->rtr_blobs;
56275703
transaction->rtr_blobs = blob;
56285704

5629-
Firebird::IBlob* b = FB_NEW Blob(blob);
5630-
b->addRef();
5631-
return b;
5705+
Blob* iBlob = FB_NEW Blob(blob);
5706+
iBlob->addRef();
5707+
5708+
if (port->port_flags & PORT_lazy)
5709+
{
5710+
// Receive two more responses. Ignore errors here, let client to receive
5711+
// and handle it later, when/if it runs corresponding action by itself.
5712+
5713+
P_RESP* response = &packet->p_resp;
5714+
// receive blob info
5715+
try
5716+
{
5717+
UsePreallocatedBuffer temp(response->p_resp_data, sizeof(infoBuffer), infoBuffer);
5718+
5719+
receive_response(status, rdb, packet);
5720+
blob->rbl_info.parseInfo(sizeof(infoBuffer), infoBuffer);
5721+
}
5722+
catch (const Exception&)
5723+
{ }
5724+
5725+
// receive blob data
5726+
try
5727+
{
5728+
UsePreallocatedBuffer temp(response->p_resp_data, blob->rbl_buffer_length, blob->rbl_buffer);
5729+
5730+
receive_response(status, rdb, packet);
5731+
5732+
blob->rbl_length = (USHORT) response->p_resp_data.cstr_length;
5733+
blob->rbl_ptr = blob->rbl_buffer;
5734+
5735+
if (response->p_resp_object == 1)
5736+
blob->rbl_flags |= Rbl::SEGMENT;
5737+
else if (response->p_resp_object == 2)
5738+
blob->rbl_flags |= Rbl::EOF_PENDING;
5739+
}
5740+
catch (const Exception&)
5741+
{ }
5742+
}
5743+
5744+
return iBlob;
56325745
}
56335746
catch (const Exception& ex)
56345747
{

src/remote/remote.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "../common/os/mod_loader.h"
3939
#include "../jrd/license.h"
4040
#include "../common/classes/ImplementHelper.h"
41+
#include "../common/utils_proto.h"
4142

4243
#ifdef DEV_BUILD
4344
Firebird::AtomicCounter rem_port::portCounter;
@@ -855,6 +856,87 @@ ISC_STATUS* Rdb::get_status_vector() throw()
855856
}
856857
*/
857858

859+
860+
bool RBlobInfo::getLocalInfo(unsigned int itemsLength, const unsigned char* items,
861+
unsigned int bufferLength, unsigned char* buffer)
862+
{
863+
if (!valid)
864+
return false;
865+
866+
unsigned char* p = buffer;
867+
const unsigned char* const end = buffer + bufferLength;
868+
869+
for (auto item = items; p && (item < items + itemsLength); item++)
870+
{
871+
switch (*item)
872+
{
873+
case isc_info_blob_num_segments:
874+
p = fb_utils::putInfoItemInt(*item, num_segments, p, end);
875+
break;
876+
877+
case isc_info_blob_max_segment:
878+
p = fb_utils::putInfoItemInt(*item, max_segment, p, end);
879+
break;
880+
881+
case isc_info_blob_total_length:
882+
p = fb_utils::putInfoItemInt(*item, total_length, p, end);
883+
break;
884+
885+
case isc_info_blob_type:
886+
p = fb_utils::putInfoItemInt(*item, blob_type, p, end);
887+
break;
888+
889+
case isc_info_end:
890+
if (p < end)
891+
*p++ = isc_info_end;
892+
break;
893+
894+
default:
895+
// unknown info item, let remote server handle it
896+
return false;
897+
}
898+
}
899+
900+
return true;
901+
}
902+
903+
904+
void RBlobInfo::parseInfo(unsigned int bufferLength, const unsigned char* buffer)
905+
{
906+
int c = 0;
907+
valid = false;
908+
909+
Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, bufferLength);
910+
for (; !p.isEof(); p.moveNext())
911+
{
912+
switch (p.getClumpTag())
913+
{
914+
case isc_info_blob_num_segments:
915+
num_segments = p.getInt();
916+
c++;
917+
break;
918+
case isc_info_blob_max_segment:
919+
max_segment = p.getInt();
920+
c++;
921+
break;
922+
case isc_info_blob_total_length:
923+
total_length = p.getInt();
924+
c++;
925+
break;
926+
case isc_info_blob_type:
927+
blob_type = p.getInt();
928+
c++;
929+
break;
930+
case isc_info_end:
931+
break;
932+
default:
933+
fb_assert(false);
934+
break;
935+
}
936+
}
937+
valid = (c == 4);
938+
}
939+
858940
void Rrq::saveStatus(const Firebird::Exception& ex) throw()
859941
{
860942
if (rrqStatus.isSuccess())

src/remote/remote.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,27 @@ struct Rtr : public Firebird::GlobalStorage, public TypedHandle<rem_type_rtr>
221221
};
222222

223223

224+
struct RBlobInfo
225+
{
226+
bool valid;
227+
UCHAR blob_type;
228+
ULONG num_segments;
229+
ULONG max_segment;
230+
ULONG total_length;
231+
232+
RBlobInfo()
233+
{
234+
memset(this, 0, sizeof(*this));
235+
}
236+
237+
// parse into response into m_info, assume buffer contains all known info items
238+
void parseInfo(unsigned int bufferLength, const unsigned char* buffer);
239+
240+
// returns false if there is no valid local info or if unknown item encountered
241+
bool getLocalInfo(unsigned int itemsLength, const unsigned char* items,
242+
unsigned int bufferLength, unsigned char* buffer);
243+
};
244+
224245
struct Rbl : public Firebird::GlobalStorage, public TypedHandle<rem_type_rbl>
225246
{
226247
Firebird::HalfStaticArray<UCHAR, BLOB_LENGTH> rbl_data;
@@ -239,6 +260,7 @@ struct Rbl : public Firebird::GlobalStorage, public TypedHandle<rem_type_rbl>
239260
USHORT rbl_source_interp; // source interp (for writing)
240261
USHORT rbl_target_interp; // destination interp (for reading)
241262
Rbl** rbl_self;
263+
RBlobInfo rbl_info;
242264

243265
public:
244266
// Values for rbl_flags

0 commit comments

Comments
 (0)