Skip to content

Commit e78b721

Browse files
committed
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 * Use 'if constexpr' as @TreeHunter9 suggested. Also, avoid hardcoded constants when possible.
1 parent 79583d6 commit e78b721

File tree

4 files changed

+265
-4
lines changed

4 files changed

+265
-4
lines changed

src/common/utils_proto.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@
3131

3232
#include <cctype>
3333
#include <string.h>
34+
#include <type_traits>
35+
3436
#include "../common/classes/fb_string.h"
3537
#include "../common/classes/array.h"
3638
#include "iberror.h"
3739
#include "firebird/Interface.h"
40+
#include "memory_routines.h"
3841

3942
#ifdef SFIO
4043
#include <stdio.h>
@@ -271,6 +274,47 @@ namespace fb_utils
271274
// Frequently used actions with clumplets
272275
bool isBpbSegmented(unsigned parLength, const unsigned char* par);
273276

277+
278+
// Put integer value into info buffer
279+
template<typename T>
280+
inline unsigned char* putInfoItemInt(const unsigned char item, T value,
281+
unsigned char* ptr, const unsigned char* end)
282+
{
283+
static_assert(std::is_integral_v<T>, "Integral type expected");
284+
285+
constexpr auto len = sizeof(T);
286+
287+
if (ptr + len + 1 + 2 > end)
288+
{
289+
if (ptr < end)
290+
{
291+
*ptr++ = isc_info_truncated;
292+
if (ptr < end)
293+
*ptr++ = isc_info_end;
294+
}
295+
return nullptr;
296+
}
297+
298+
*ptr++ = item;
299+
*ptr++ = len;
300+
*ptr++ = 0;
301+
302+
if constexpr (len == sizeof(SINT64))
303+
put_vax_int64(ptr, value);
304+
else if constexpr (len == sizeof(SLONG))
305+
put_vax_long(ptr, value);
306+
else if constexpr (len == sizeof(SSHORT))
307+
put_vax_short(ptr, value);
308+
else if constexpr (len == sizeof(char))
309+
*ptr = value;
310+
else
311+
static_assert(always_false<T>::value, "unknown data type");
312+
313+
ptr += len;
314+
return ptr;
315+
}
316+
317+
274318
// RAII to call fb_shutdown() in utilities
275319
class FbShutdown
276320
{

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

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

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

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

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

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() noexcept
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) noexcept
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)