diff --git a/pgxn/neon/libpagestore.c b/pgxn/neon/libpagestore.c index 8eb9ebb915..e2a59817e9 100644 --- a/pgxn/neon/libpagestore.c +++ b/pgxn/neon/libpagestore.c @@ -18,6 +18,7 @@ #include "fmgr.h" #include "access/xlog.h" #include "access/xlogutils.h" +#include "common/hashfn.h" #include "storage/buf_internals.h" #include "storage/lwlock.h" #include "storage/ipc.h" @@ -36,22 +37,12 @@ #include "neon.h" #include "walproposer.h" #include "neon_utils.h" +#include "control_plane_connector.h" #define PageStoreTrace DEBUG5 #define RECONNECT_INTERVAL_USEC 1000000 -bool connected = false; -PGconn *pageserver_conn = NULL; - -/* - * WaitEventSet containing: - * - WL_SOCKET_READABLE on pageserver_conn, - * - WL_LATCH_SET on MyLatch, and - * - WL_EXIT_ON_PM_DEATH. - */ -WaitEventSet *pageserver_conn_wes = NULL; - /* GUCs */ char *neon_timeline; char *neon_tenant; @@ -64,87 +55,212 @@ int flush_every_n_requests = 8; int n_reconnect_attempts = 0; int max_reconnect_attempts = 60; - -#define MAX_PAGESERVER_CONNSTRING_SIZE 256 - -typedef struct -{ - LWLockId lock; - pg_atomic_uint64 update_counter; - char pageserver_connstring[MAX_PAGESERVER_CONNSTRING_SIZE]; -} PagestoreShmemState; - -#if PG_VERSION_NUM >= 150000 -static shmem_request_hook_type prev_shmem_request_hook = NULL; -static void walproposer_shmem_request(void); -#endif -static shmem_startup_hook_type prev_shmem_startup_hook; -static PagestoreShmemState *pagestore_shared; -static uint64 pagestore_local_counter = 0; -static char local_pageserver_connstring[MAX_PAGESERVER_CONNSTRING_SIZE]; +int stripe_size; bool (*old_redo_read_buffer_filter) (XLogReaderState *record, uint8 block_id) = NULL; -static bool pageserver_flush(void); -static void pageserver_disconnect(void); +static bool pageserver_flush(shardno_t shard_no); +static void pageserver_disconnect(shardno_t shard_no); -static bool -PagestoreShmemIsValid() -{ - return pagestore_shared && UsedShmemSegAddr; -} +static shmem_startup_hook_type prev_shmem_startup_hook; +#if PG_VERSION_NUM>=150000 +static shmem_request_hook_type prev_shmem_request_hook; +#endif -static bool -CheckPageserverConnstring(char **newval, void **extra, GucSource source) +typedef struct { - return strlen(*newval) < MAX_PAGESERVER_CONNSTRING_SIZE; + size_t n_shards; + size_t update_counter; + char shard_connstr[MAX_SHARDS][MAX_PS_CONNSTR_LEN]; +} ShardMap; + + +static ShardMap* shard_map; +static LWLockId shard_map_lock; +static size_t shard_map_update_counter; +static bool config_updated; + +typedef struct +{ + /* + * connection for each shard + */ + PGconn *conn; + /* + * WaitEventSet containing: + * - WL_SOCKET_READABLE on pageserver_conn, + * - WL_LATCH_SET on MyLatch, and + * - WL_EXIT_ON_PM_DEATH. + */ + WaitEventSet *wes; +} PageServer; + +static PageServer page_servers[MAX_SHARDS]; +static shardno_t max_attached_shard_no; + +static void +psm_shmem_startup(void) +{ + bool found; + if (prev_shmem_startup_hook) + { + prev_shmem_startup_hook(); + } + + LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + + shard_map = (ShardMap*)ShmemInitStruct("shard_map", sizeof(ShardMap), &found); + if (!found) + { + shard_map_lock = (LWLockId)GetNamedLWLockTranche("shard_map_lock"); + shard_map->n_shards = 0; + shard_map->update_counter = 1; /* force update of shared map on forst access */ + } + LWLockRelease(AddinShmemInitLock); } static void -AssignPageserverConnstring(const char *newval, void *extra) +psm_shmem_request(void) { - if(!PagestoreShmemIsValid()) - return; - LWLockAcquire(pagestore_shared->lock, LW_EXCLUSIVE); - strlcpy(pagestore_shared->pageserver_connstring, newval, MAX_PAGESERVER_CONNSTRING_SIZE); - pg_atomic_fetch_add_u64(&pagestore_shared->update_counter, 1); - LWLockRelease(pagestore_shared->lock); -} +#if PG_VERSION_NUM>=150000 + if (prev_shmem_request_hook) + prev_shmem_request_hook(); +#endif -static bool -CheckConnstringUpdated() -{ - if(!PagestoreShmemIsValid()) - return false; - return pagestore_local_counter < pg_atomic_read_u64(&pagestore_shared->update_counter); + RequestAddinShmemSpace(sizeof(ShardMap)); + RequestNamedLWLockTranche("shard_map_lock", 1); } static void -ReloadConnstring() +psm_init(void) { - if(!PagestoreShmemIsValid()) - return; - LWLockAcquire(pagestore_shared->lock, LW_SHARED); - strlcpy(local_pageserver_connstring, pagestore_shared->pageserver_connstring, sizeof(local_pageserver_connstring)); - pagestore_local_counter = pg_atomic_read_u64(&pagestore_shared->update_counter); - LWLockRelease(pagestore_shared->lock); + prev_shmem_startup_hook = shmem_startup_hook; + shmem_startup_hook = psm_shmem_startup; +#if PG_VERSION_NUM>=150000 + prev_shmem_request_hook = shmem_request_hook; + shmem_request_hook = psm_shmem_request; +#else + psm_shmem_request(); +#endif +} + +/* + * Reload page map if needed and return number of shards and connection string for the specified shard + */ +static shardno_t +load_shard_map(shardno_t shard_no, char* connstr) +{ + shardno_t n_shards; + bool exclusive_lock = false; + LWLockAcquire(shard_map_lock, LW_SHARED); + + if (shard_map_update_counter != shard_map->update_counter || config_updated) + { + ParseConnString: + { + char const* shard_connstr = page_server_connstring; + char const* sep; + size_t connstr_len; + int i = 0; + bool shard_map_changed = false; + do + { + sep = strchr(shard_connstr, ','); + connstr_len = sep != NULL ? sep - shard_connstr : strlen(shard_connstr); + if (connstr_len == 0) + break; /* trailing comma */ + if (i >= MAX_SHARDS) + elog(ERROR, "Too many shards"); + if (connstr_len >= MAX_PS_CONNSTR_LEN) + elog(ERROR, "Connection string too long"); + if (i >= shard_map->n_shards || + strcmp(shard_map->shard_connstr[i], shard_connstr) != 0) + { + if (!exclusive_lock) + { + /* Retry under exclsuive lock */ + LWLockRelease(shard_map_lock); + LWLockAcquire(shard_map_lock, LW_EXCLUSIVE); + exclusive_lock = true; + goto ParseConnString; + } + shard_map_changed = true; + memcpy(shard_map->shard_connstr[i], shard_connstr, connstr_len+1); + } + shard_connstr = sep + 1; + i += 1; + } while (sep != NULL); + + if (i == 0) + elog(ERROR, "No shards were speciified"); + + if (shard_map_changed) + { + shard_map->update_counter += 1; + shard_map->n_shards = i; + } + } + if (shard_map_update_counter != shard_map->update_counter) + { + for (shardno_t i = 0; i < max_attached_shard_no; i++) + { + if (page_servers[i].conn) + pageserver_disconnect(i); + } + shard_map_update_counter = shard_map->update_counter; + } + config_updated = false; + } + n_shards = shard_map->n_shards; + if (shard_no >= n_shards) + elog(ERROR, "Shard %d is greater or equal than number of shards %d", shard_no, n_shards); + + if (connstr) + strcpy(connstr, shard_map->shard_connstr[shard_no]); + + LWLockRelease(shard_map_lock); + + return n_shards; +} + +#define MB (1024*1024) + +shardno_t +get_shard_number(BufferTag* tag) +{ + shardno_t n_shards = load_shard_map(0, NULL); + uint32 hash; + +#if PG_MAJORVERSION_NUM < 16 + hash = murmurhash32(tag->rnode.spcNode); + hash = hash_combine(hash, murmurhash32(tag->rnode.dbNode)); + hash = hash_combine(hash, murmurhash32(tag->rnode.relNode)); + hash = hash_combine(hash, murmurhash32(tag->blockNum/(MB/BLCKSZ)/stripe_size)); +#else + hash = murmurhash32(tag->spcOid); + hash = hash_combine(hash, murmurhash32(tag->dbOid)); + hash = hash_combine(hash, murmurhash32(tag->relNumber)); + hash = hash_combine(hash, murmurhash32(tag->blockNum/(MB/BLCKSZ)/stripe_size)); +#endif + + return hash % n_shards; } static bool -pageserver_connect(int elevel) +pageserver_connect(shardno_t shard_no, int elevel) { char *query; int ret; const char *keywords[3]; const char *values[3]; int n; + PGconn* conn; + WaitEventSet *wes; + char connstr[MAX_PS_CONNSTR_LEN]; - Assert(!connected); + Assert(page_servers[shard_no].conn == NULL); - if(CheckConnstringUpdated()) - { - ReloadConnstring(); - } + (void)load_shard_map(shard_no, connstr); /* refresh page map if needed */ /* * Connect using the connection string we got from the @@ -165,19 +281,18 @@ pageserver_connect(int elevel) n++; } keywords[n] = "dbname"; - values[n] = local_pageserver_connstring; + values[n] = connstr; n++; keywords[n] = NULL; values[n] = NULL; n++; - pageserver_conn = PQconnectdbParams(keywords, values, 1); + conn = PQconnectdbParams(keywords, values, 1); - if (PQstatus(pageserver_conn) == CONNECTION_BAD) + if (PQstatus(conn) == CONNECTION_BAD) { - char *msg = pchomp(PQerrorMessage(pageserver_conn)); + char *msg = pchomp(PQerrorMessage(conn)); - PQfinish(pageserver_conn); - pageserver_conn = NULL; + PQfinish(conn); ereport(elevel, (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), @@ -185,30 +300,28 @@ pageserver_connect(int elevel) errdetail_internal("%s", msg))); return false; } - query = psprintf("pagestream %s %s", neon_tenant, neon_timeline); - ret = PQsendQuery(pageserver_conn, query); + ret = PQsendQuery(conn, query); if (ret != 1) { - PQfinish(pageserver_conn); - pageserver_conn = NULL; + PQfinish(conn); neon_log(elevel, "could not send pagestream command to pageserver"); return false; } - pageserver_conn_wes = CreateWaitEventSet(TopMemoryContext, 3); - AddWaitEventToSet(pageserver_conn_wes, WL_LATCH_SET, PGINVALID_SOCKET, + wes = CreateWaitEventSet(TopMemoryContext, 3); + AddWaitEventToSet(wes, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL); - AddWaitEventToSet(pageserver_conn_wes, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET, + AddWaitEventToSet(wes, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET, NULL, NULL); - AddWaitEventToSet(pageserver_conn_wes, WL_SOCKET_READABLE, PQsocket(pageserver_conn), NULL, NULL); + AddWaitEventToSet(wes, WL_SOCKET_READABLE, PQsocket(conn), NULL, NULL); - while (PQisBusy(pageserver_conn)) + while (PQisBusy(conn)) { WaitEvent event; /* Sleep until there's something to do */ - (void) WaitEventSetWait(pageserver_conn_wes, -1L, &event, 1, PG_WAIT_EXTENSION); + (void) WaitEventSetWait(wes, -1L, &event, 1, PG_WAIT_EXTENSION); ResetLatch(MyLatch); CHECK_FOR_INTERRUPTS(); @@ -216,14 +329,12 @@ pageserver_connect(int elevel) /* Data available in socket? */ if (event.events & WL_SOCKET_READABLE) { - if (!PQconsumeInput(pageserver_conn)) + if (!PQconsumeInput(conn)) { - char *msg = pchomp(PQerrorMessage(pageserver_conn)); + char *msg = pchomp(PQerrorMessage(conn)); - PQfinish(pageserver_conn); - pageserver_conn = NULL; - FreeWaitEventSet(pageserver_conn_wes); - pageserver_conn_wes = NULL; + PQfinish(conn); + FreeWaitEventSet(wes); neon_log(elevel, "could not complete handshake with pageserver: %s", msg); @@ -232,9 +343,11 @@ pageserver_connect(int elevel) } } - neon_log(LOG, "libpagestore: connected to '%s'", page_server_connstring); + neon_log(LOG, "libpagestore: connected to '%s'", connstr); + page_servers[shard_no].conn = conn; + page_servers[shard_no].wes = wes; + max_attached_shard_no = Max(shard_no+1, max_attached_shard_no); - connected = true; return true; } @@ -242,10 +355,10 @@ pageserver_connect(int elevel) * A wrapper around PQgetCopyData that checks for interrupts while sleeping. */ static int -call_PQgetCopyData(char **buffer) +call_PQgetCopyData(shardno_t shard_no, char **buffer) { int ret; - + PGconn* pageserver_conn = page_servers[shard_no].conn; retry: ret = PQgetCopyData(pageserver_conn, buffer, 1 /* async */ ); @@ -254,7 +367,7 @@ retry: WaitEvent event; /* Sleep until there's something to do */ - (void) WaitEventSetWait(pageserver_conn_wes, -1L, &event, 1, PG_WAIT_EXTENSION); + (void) WaitEventSetWait(page_servers[shard_no].wes, -1L, &event, 1, PG_WAIT_EXTENSION); ResetLatch(MyLatch); CHECK_FOR_INTERRUPTS(); @@ -279,7 +392,7 @@ retry: static void -pageserver_disconnect(void) +pageserver_disconnect(shardno_t shard_no) { /* * If anything goes wrong while we were sending a request, it's not clear @@ -288,38 +401,32 @@ pageserver_disconnect(void) * time later after we have already sent a new unrelated request. Close * the connection to avoid getting confused. */ - if (connected) + if (page_servers[shard_no].conn) { neon_log(LOG, "dropping connection to page server due to error"); - PQfinish(pageserver_conn); - pageserver_conn = NULL; - connected = false; + PQfinish(page_servers[shard_no].conn); + page_servers[shard_no].conn = NULL; prefetch_on_ps_disconnect(); } - if (pageserver_conn_wes != NULL) + if (page_servers[shard_no].wes != NULL) { - FreeWaitEventSet(pageserver_conn_wes); - pageserver_conn_wes = NULL; + FreeWaitEventSet(page_servers[shard_no].wes); + page_servers[shard_no].wes = NULL; } } static bool -pageserver_send(NeonRequest * request) +pageserver_send(shardno_t shard_no, NeonRequest * request) { StringInfoData req_buff; - - if(CheckConnstringUpdated()) - { - pageserver_disconnect(); - ReloadConnstring(); - } + PGconn* pageserver_conn = page_servers[shard_no].conn; /* If the connection was lost for some reason, reconnect */ - if (connected && PQstatus(pageserver_conn) == CONNECTION_BAD) + if (pageserver_conn && PQstatus(pageserver_conn) == CONNECTION_BAD) { neon_log(LOG, "pageserver_send disconnect bad connection"); - pageserver_disconnect(); + pageserver_disconnect(shard_no); } req_buff = nm_pack_request(request); @@ -331,9 +438,9 @@ pageserver_send(NeonRequest * request) * See https://github.com/neondatabase/neon/issues/1138 * So try to reestablish connection in case of failure. */ - if (!connected) + if (!page_servers[shard_no].conn) { - while (!pageserver_connect(n_reconnect_attempts < max_reconnect_attempts ? LOG : ERROR)) + while (!pageserver_connect(shard_no, n_reconnect_attempts < max_reconnect_attempts ? LOG : ERROR)) { HandleMainLoopInterrupts(); n_reconnect_attempts += 1; @@ -342,7 +449,9 @@ pageserver_send(NeonRequest * request) n_reconnect_attempts = 0; } - /* + pageserver_conn = page_servers[shard_no].conn; + + /* * Send request. * * In principle, this could block if the output buffer is full, and we @@ -353,7 +462,7 @@ pageserver_send(NeonRequest * request) if (PQputCopyData(pageserver_conn, req_buff.data, req_buff.len) <= 0) { char *msg = pchomp(PQerrorMessage(pageserver_conn)); - pageserver_disconnect(); + pageserver_disconnect(shard_no); neon_log(LOG, "pageserver_send disconnect because failed to send page request (try to reconnect): %s", msg); pfree(msg); pfree(req_buff.data); @@ -373,12 +482,12 @@ pageserver_send(NeonRequest * request) } static NeonResponse * -pageserver_receive(void) +pageserver_receive(shardno_t shard_no) { StringInfoData resp_buff; NeonResponse *resp; - - if (!connected) + PGconn* pageserver_conn = page_servers[shard_no].conn; + if (!pageserver_conn) return NULL; PG_TRY(); @@ -386,7 +495,7 @@ pageserver_receive(void) /* read response */ int rc; - rc = call_PQgetCopyData(&resp_buff.data); + rc = call_PQgetCopyData(shard_no, &resp_buff.data); if (rc >= 0) { resp_buff.len = rc; @@ -405,25 +514,25 @@ pageserver_receive(void) else if (rc == -1) { neon_log(LOG, "pageserver_receive disconnect because call_PQgetCopyData returns -1: %s", pchomp(PQerrorMessage(pageserver_conn))); - pageserver_disconnect(); + pageserver_disconnect(shard_no); resp = NULL; } else if (rc == -2) { char* msg = pchomp(PQerrorMessage(pageserver_conn)); - pageserver_disconnect(); + pageserver_disconnect(shard_no); neon_log(ERROR, "pageserver_receive disconnect because could not read COPY data: %s", msg); } else { - pageserver_disconnect(); + pageserver_disconnect(shard_no); neon_log(ERROR, "pageserver_receive disconnect because unexpected PQgetCopyData return value: %d", rc); } } PG_CATCH(); { neon_log(LOG, "pageserver_receive disconnect due to caught exception"); - pageserver_disconnect(); + pageserver_disconnect(shard_no); PG_RE_THROW(); } PG_END_TRY(); @@ -433,9 +542,10 @@ pageserver_receive(void) static bool -pageserver_flush(void) +pageserver_flush(shardno_t shard_no) { - if (!connected) + PGconn* pageserver_conn = page_servers[shard_no].conn; + if (!pageserver_conn) { neon_log(WARNING, "Tried to flush while disconnected"); } @@ -444,7 +554,7 @@ pageserver_flush(void) if (PQflush(pageserver_conn)) { char *msg = pchomp(PQerrorMessage(pageserver_conn)); - pageserver_disconnect(); + pageserver_disconnect(shard_no); neon_log(LOG, "pageserver_flush disconnect because failed to flush page requests: %s", msg); pfree(msg); return false; @@ -468,62 +578,10 @@ check_neon_id(char **newval, void **extra, GucSource source) return **newval == '\0' || HexDecodeString(id, *newval, 16); } -static Size -PagestoreShmemSize(void) -{ - return sizeof(PagestoreShmemState); -} - -static bool -PagestoreShmemInit(void) -{ - bool found; - LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); - pagestore_shared = ShmemInitStruct("libpagestore shared state", - PagestoreShmemSize(), - &found); - if(!found) - { - pagestore_shared->lock = &(GetNamedLWLockTranche("neon_libpagestore")->lock); - pg_atomic_init_u64(&pagestore_shared->update_counter, 0); - AssignPageserverConnstring(page_server_connstring, NULL); - } - LWLockRelease(AddinShmemInitLock); - return found; -} - static void -pagestore_shmem_startup_hook(void) +AssignPageserverConnstring(const char *newval, void *extra) { - if(prev_shmem_startup_hook) - prev_shmem_startup_hook(); - - PagestoreShmemInit(); -} - -static void -pagestore_shmem_request(void) -{ -#if PG_VERSION_NUM >= 150000 - if(prev_shmem_request_hook) - prev_shmem_request_hook(); -#endif - - RequestAddinShmemSpace(PagestoreShmemSize()); - RequestNamedLWLockTranche("neon_libpagestore", 1); -} - -static void -pagestore_prepare_shmem(void) -{ -#if PG_VERSION_NUM >= 150000 - prev_shmem_request_hook = shmem_request_hook; - shmem_request_hook = pagestore_shmem_request; -#else - pagestore_shmem_request(); -#endif - prev_shmem_startup_hook = shmem_startup_hook; - shmem_startup_hook = pagestore_shmem_startup_hook; + config_updated = true; } /* @@ -532,8 +590,6 @@ pagestore_prepare_shmem(void) void pg_init_libpagestore(void) { - pagestore_prepare_shmem(); - DefineCustomStringVariable("neon.pageserver_connstring", "connection string to the page server", NULL, @@ -541,7 +597,7 @@ pg_init_libpagestore(void) "", PGC_SIGHUP, 0, /* no flags required */ - CheckPageserverConnstring, AssignPageserverConnstring, NULL); + NULL, AssignPageserverConnstring, NULL); DefineCustomStringVariable("neon.timeline_id", "Neon timeline_id the server is running on", @@ -561,6 +617,15 @@ pg_init_libpagestore(void) 0, /* no flags required */ check_neon_id, NULL, NULL); + DefineCustomIntVariable("neon.stripe_size", + "sharding sripe size", + NULL, + &stripe_size, + 256, 1, INT_MAX, + PGC_SIGHUP, + GUC_UNIT_MB, + NULL, NULL, NULL); + DefineCustomIntVariable("neon.max_cluster_size", "cluster size limit", NULL, @@ -623,4 +688,5 @@ pg_init_libpagestore(void) } lfc_init(); + psm_init(); } diff --git a/pgxn/neon/pagestore_client.h b/pgxn/neon/pagestore_client.h index d61f74b5c8..1a39b5a9ac 100644 --- a/pgxn/neon/pagestore_client.h +++ b/pgxn/neon/pagestore_client.h @@ -20,12 +20,16 @@ #include RELFILEINFO_HDR #include "storage/block.h" #include "storage/smgr.h" +#include "storage/buf_internals.h" #include "lib/stringinfo.h" #include "libpq/pqformat.h" #include "utils/memutils.h" #include "pg_config.h" +#define MAX_SHARDS 128 +#define MAX_PS_CONNSTR_LEN 128 + typedef enum { /* pagestore_client -> pagestore */ @@ -144,11 +148,13 @@ extern char *nm_to_string(NeonMessage * msg); * API */ +typedef unsigned shardno_t; + typedef struct { - bool (*send) (NeonRequest * request); - NeonResponse *(*receive) (void); - bool (*flush) (void); + bool (*send) (shardno_t shard_no, NeonRequest * request); + NeonResponse *(*receive) (shardno_t shard_no); + bool (*flush) (shardno_t shard_no); } page_server_api; extern void prefetch_on_ps_disconnect(void); @@ -165,6 +171,8 @@ extern char *neon_tenant; extern bool wal_redo; extern int32 max_cluster_size; +extern shardno_t get_shard_number(BufferTag* tag); + extern const f_smgr *smgr_neon(BackendId backend, NRelFileInfo rinfo); extern void smgr_init_neon(void); extern void readahead_buffer_resize(int newsize, void *extra); diff --git a/pgxn/neon/pagestore_smgr.c b/pgxn/neon/pagestore_smgr.c index 84b26198a7..871943092f 100644 --- a/pgxn/neon/pagestore_smgr.c +++ b/pgxn/neon/pagestore_smgr.c @@ -164,6 +164,7 @@ typedef struct PrefetchRequest { XLogRecPtr actual_request_lsn; NeonResponse *response; /* may be null */ PrefetchStatus status; + shardno_t shard_no; uint64 my_ring_index; } PrefetchRequest; @@ -225,6 +226,8 @@ typedef struct PrefetchState { /* the buffers */ prfh_hash *prf_hash; + int max_shard_no; + uint8 shard_bitmap[(MAX_SHARDS + 7)/8]; PrefetchRequest prf_buffer[]; /* prefetch buffers */ } PrefetchState; @@ -313,6 +316,7 @@ compact_prefetch_buffers(void) Assert(target_slot->status == PRFS_UNUSED); target_slot->buftag = source_slot->buftag; + target_slot->shard_no = source_slot->shard_no; target_slot->status = source_slot->status; target_slot->response = source_slot->response; target_slot->effective_request_lsn = source_slot->effective_request_lsn; @@ -477,6 +481,23 @@ prefetch_cleanup_trailing_unused(void) } } + +static bool +prefetch_flush_requests(void) +{ + for (shardno_t shard_no = 0; shard_no < MyPState->max_shard_no; shard_no++) + { + if (MyPState->shard_bitmap[shard_no >> 3] & (1 << (shard_no & 7))) + { + if (!page_server->flush(shard_no)) + return false; + MyPState->shard_bitmap[shard_no >> 3] &= ~(1 << (shard_no & 7)); + } + } + MyPState->max_shard_no = 0; + return true; +} + /* * Wait for slot of ring_index to have received its response. * The caller is responsible for making sure the request buffer is flushed. @@ -492,7 +513,7 @@ prefetch_wait_for(uint64 ring_index) if (MyPState->ring_flush <= ring_index && MyPState->ring_unused > MyPState->ring_flush) { - if (!page_server->flush()) + if (!prefetch_flush_requests()) return false; MyPState->ring_flush = MyPState->ring_unused; } @@ -530,7 +551,7 @@ prefetch_read(PrefetchRequest *slot) Assert(slot->my_ring_index == MyPState->ring_receive); old = MemoryContextSwitchTo(MyPState->errctx); - response = (NeonResponse *) page_server->receive(); + response = (NeonResponse *) page_server->receive(slot->shard_no); MemoryContextSwitchTo(old); if (response) { @@ -682,12 +703,14 @@ prefetch_do_request(PrefetchRequest *slot, bool *force_latest, XLogRecPtr *force Assert(slot->response == NULL); Assert(slot->my_ring_index == MyPState->ring_unused); - while (!page_server->send((NeonRequest *) &request)); + while (!page_server->send(slot->shard_no, (NeonRequest *) &request)); /* update prefetch state */ MyPState->n_requests_inflight += 1; MyPState->n_unused -= 1; MyPState->ring_unused += 1; + MyPState->shard_bitmap[slot->shard_no >> 3] |= 1 << (slot->shard_no & 7); + MyPState->max_shard_no = Max(slot->shard_no+1, MyPState->max_shard_no); /* update slot state */ slot->status = PRFS_REQUESTED; @@ -847,6 +870,7 @@ prefetch_register_buffer(BufferTag tag, bool *force_latest, XLogRecPtr *force_ls * function reads the buffer tag from the slot. */ slot->buftag = tag; + slot->shard_no = get_shard_number(&tag); slot->my_ring_index = ring_index; prefetch_do_request(slot, force_latest, force_lsn); @@ -857,7 +881,7 @@ prefetch_register_buffer(BufferTag tag, bool *force_latest, XLogRecPtr *force_ls if (flush_every_n_requests > 0 && MyPState->ring_unused - MyPState->ring_flush >= flush_every_n_requests) { - if (!page_server->flush()) + if (!prefetch_flush_requests()) { /* Prefetch set is reset in case of error, so we should try to register our request once again */ goto Retry; @@ -872,11 +896,43 @@ static NeonResponse * page_server_request(void const *req) { NeonResponse* resp; + BufferTag tag = {0}; + shardno_t shard_no; + + switch (((NeonRequest *) req)->tag) + { + case T_NeonExistsRequest: + CopyNRelFileInfoToBufTag(tag, ((NeonExistsRequest *) req)->rinfo); + break; + case T_NeonNblocksRequest: + CopyNRelFileInfoToBufTag(tag, ((NeonNblocksRequest *) req)->rinfo); + break; + case T_NeonDbSizeRequest: + NInfoGetDbOid(BufTagGetNRelFileInfo(tag)) = ((NeonDbSizeRequest *) req)->dbNode; + break; + case T_NeonGetPageRequest: + CopyNRelFileInfoToBufTag(tag, ((NeonGetPageRequest *) req)->rinfo); + tag.blockNum = ((NeonGetPageRequest *) req)->blkno; + break; + default: + elog(ERROR, "Unexpected request tag: %d", ((NeonRequest *) req)->tag); + } + shard_no = get_shard_number(&tag); + + /* + * TODO: temporary workarround - we stream all WAL only to shard 0 so metadata and forks other than main + * should be requested from shard 0. We still need to call get_shard_no() to check if shard map is up-to-date + */ + if (((NeonRequest *) req)->tag != T_NeonGetPageRequest || ((NeonGetPageRequest *) req)->forknum != MAIN_FORKNUM) + { + shard_no = 0; + } + do { - while (!page_server->send((NeonRequest *) req) || !page_server->flush()); + while (!page_server->send(shard_no, (NeonRequest *) req) || !page_server->flush(shard_no)); MyPState->ring_flush = MyPState->ring_unused; consume_prefetch_responses(); - resp = page_server->receive(); + resp = page_server->receive(shard_no); } while (resp == NULL); return resp;