Before this change on first connect between primary and secondary we
initialize all the data. This is huge waste of time and resources if there were no writes yet, as there is no real data to synchronize. Optimize this by sending "virgin" argument to secondary, which gives it a hint that synchronization is not needed. In the common case (where noth nodes are configured at the same time) instead of synchronizing everything, we don't synchronize at all. MFC after: 1 week
This commit is contained in:
parent
b9ffbb0a94
commit
ce837469ba
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=214284
@ -417,6 +417,24 @@ init_environment(struct hast_resource *res __unused)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
init_resuid(struct hast_resource *res)
|
||||||
|
{
|
||||||
|
|
||||||
|
mtx_lock(&metadata_lock);
|
||||||
|
if (res->hr_resuid != 0) {
|
||||||
|
mtx_unlock(&metadata_lock);
|
||||||
|
return (false);
|
||||||
|
} else {
|
||||||
|
/* Initialize unique resource identifier. */
|
||||||
|
arc4random_buf(&res->hr_resuid, sizeof(res->hr_resuid));
|
||||||
|
mtx_unlock(&metadata_lock);
|
||||||
|
if (metadata_write(res) < 0)
|
||||||
|
exit(EX_NOINPUT);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_local(struct hast_resource *res)
|
init_local(struct hast_resource *res)
|
||||||
{
|
{
|
||||||
@ -452,10 +470,12 @@ init_local(struct hast_resource *res)
|
|||||||
if (res->hr_resuid != 0)
|
if (res->hr_resuid != 0)
|
||||||
return;
|
return;
|
||||||
/*
|
/*
|
||||||
* We're using provider for the first time, so we have to generate
|
* We're using provider for the first time. Initialize local and remote
|
||||||
* resource unique identifier and initialize local and remote counts.
|
* counters. We don't initialize resuid here, as we want to do it just
|
||||||
|
* in time. The reason for this is that we want to inform secondary
|
||||||
|
* that there were no writes yet, so there is no need to synchronize
|
||||||
|
* anything.
|
||||||
*/
|
*/
|
||||||
arc4random_buf(&res->hr_resuid, sizeof(res->hr_resuid));
|
|
||||||
res->hr_primary_localcnt = 1;
|
res->hr_primary_localcnt = 1;
|
||||||
res->hr_primary_remotecnt = 0;
|
res->hr_primary_remotecnt = 0;
|
||||||
if (metadata_write(res) < 0)
|
if (metadata_write(res) < 0)
|
||||||
@ -566,6 +586,19 @@ init_remote(struct hast_resource *res, struct proto_conn **inp,
|
|||||||
nv_add_string(nvout, res->hr_name, "resource");
|
nv_add_string(nvout, res->hr_name, "resource");
|
||||||
nv_add_uint8_array(nvout, res->hr_token, sizeof(res->hr_token),
|
nv_add_uint8_array(nvout, res->hr_token, sizeof(res->hr_token),
|
||||||
"token");
|
"token");
|
||||||
|
if (res->hr_resuid == 0) {
|
||||||
|
/*
|
||||||
|
* The resuid field was not yet initialized.
|
||||||
|
* Because we do synchronization inside init_resuid(), it is
|
||||||
|
* possible that someone already initialized it, the function
|
||||||
|
* will return false then, but if we successfully initialized
|
||||||
|
* it, we will get true. True means that there were no writes
|
||||||
|
* to this resource yet and we want to inform secondary that
|
||||||
|
* synchronization is not needed by sending "virgin" argument.
|
||||||
|
*/
|
||||||
|
if (init_resuid(res))
|
||||||
|
nv_add_int8(nvout, 1, "virgin");
|
||||||
|
}
|
||||||
nv_add_uint64(nvout, res->hr_resuid, "resuid");
|
nv_add_uint64(nvout, res->hr_resuid, "resuid");
|
||||||
nv_add_uint64(nvout, res->hr_primary_localcnt, "localcnt");
|
nv_add_uint64(nvout, res->hr_primary_localcnt, "localcnt");
|
||||||
nv_add_uint64(nvout, res->hr_primary_remotecnt, "remotecnt");
|
nv_add_uint64(nvout, res->hr_primary_remotecnt, "remotecnt");
|
||||||
@ -1006,6 +1039,10 @@ ggate_recv_thread(void *arg)
|
|||||||
QUEUE_INSERT1(hio, send, ncomp);
|
QUEUE_INSERT1(hio, send, ncomp);
|
||||||
break;
|
break;
|
||||||
case BIO_WRITE:
|
case BIO_WRITE:
|
||||||
|
if (res->hr_resuid == 0) {
|
||||||
|
/* This is first write, initialize resuid. */
|
||||||
|
(void)init_resuid(res);
|
||||||
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
mtx_lock(&range_lock);
|
mtx_lock(&range_lock);
|
||||||
if (rangelock_islocked(range_sync,
|
if (rangelock_islocked(range_sync,
|
||||||
|
@ -243,13 +243,22 @@ init_remote(struct hast_resource *res, struct nv *nvin)
|
|||||||
*/
|
*/
|
||||||
if (res->hr_resuid == 0) {
|
if (res->hr_resuid == 0) {
|
||||||
/*
|
/*
|
||||||
* Provider is used for the first time. Initialize everything.
|
* Provider is used for the first time. If primary node done no
|
||||||
|
* writes yet as well (we will find "virgin" argument) then
|
||||||
|
* there is no need to synchronize anything. If primary node
|
||||||
|
* done any writes already we have to synchronize everything.
|
||||||
*/
|
*/
|
||||||
assert(res->hr_secondary_localcnt == 0);
|
assert(res->hr_secondary_localcnt == 0);
|
||||||
res->hr_resuid = resuid;
|
res->hr_resuid = resuid;
|
||||||
if (metadata_write(res) < 0)
|
if (metadata_write(res) < 0)
|
||||||
exit(EX_NOINPUT);
|
exit(EX_NOINPUT);
|
||||||
memset(map, 0xff, mapsize);
|
if (nv_exists(nvin, "virgin")) {
|
||||||
|
free(map);
|
||||||
|
map = NULL;
|
||||||
|
mapsize = 0;
|
||||||
|
} else {
|
||||||
|
memset(map, 0xff, mapsize);
|
||||||
|
}
|
||||||
nv_add_uint8(nvout, HAST_SYNCSRC_PRIMARY, "syncsrc");
|
nv_add_uint8(nvout, HAST_SYNCSRC_PRIMARY, "syncsrc");
|
||||||
} else if (
|
} else if (
|
||||||
/* Is primary is out-of-date? */
|
/* Is primary is out-of-date? */
|
||||||
|
Loading…
Reference in New Issue
Block a user