fix(hset_family): Fix val being overwritten by TTL

When hset is loaded from rdb, if a ttl is specified by the user, the
code recreates a string_view on top of the same memory location,
tset_blob_, that was used for val earlier. This causes the val string
view to point to the same value now as TTL has. For example if the val
string view was 'x' earlier (points to tset_blob_, size 1), and ttl is
7777777, val now becomes '7'.

To fix this val is now given it's own string. TTL is kept as string
view as the pointer is not reused anywhere in the following loop.

Signed-off-by: Abhijat Malviya <abhijat@dragonflydb.io>
This commit is contained in:
Abhijat Malviya 2025-05-10 11:08:46 +05:30
parent fc00f2c972
commit d74aff6d2a
No known key found for this signature in database
2 changed files with 31 additions and 15 deletions

View file

@ -482,11 +482,12 @@ void RdbLoaderBase::OpaqueObjLoader::CreateHMap(const LoadTrace* ltrace) {
} }
}); });
std::string key; std::string key;
std::string val;
for (size_t i = 0; i < ltrace->arr.size(); i += increment) { for (size_t i = 0; i < ltrace->arr.size(); i += increment) {
// ToSV may reference an internal buffer, therefore we can use only before the // ToSV may reference an internal buffer, therefore we can use only before the
// next call to ToSV. To workaround, copy the key locally. // next call to ToSV. To workaround, copy the key locally.
key = ToSV(ltrace->arr[i].rdb_var); key = ToSV(ltrace->arr[i].rdb_var);
string_view val = ToSV(ltrace->arr[i + 1].rdb_var); val = ToSV(ltrace->arr[i + 1].rdb_var);
if (ec_) if (ec_)
return; return;

View file

@ -1,23 +1,18 @@
import random
from itertools import chain, repeat
import re
import pytest
import asyncio
import async_timeout
import platform import platform
import pymemcache import shutil
import logging
import tarfile import tarfile
import urllib.request import urllib.request
import shutil from itertools import chain, repeat
from redis import asyncio as aioredis
from .utility import * import async_timeout
from .instance import DflyInstanceFactory, DflyInstance import pymemcache
from .seeder import Seeder as SeederV2
from . import dfly_args from . import dfly_args
from .instance import DflyInstanceFactory, DflyInstance
from .proxy import Proxy from .proxy import Proxy
from .seeder import DebugPopulateSeeder from .seeder import DebugPopulateSeeder
from .seeder import Seeder as SeederV2
from .utility import *
ADMIN_PORT = 1211 ADMIN_PORT = 1211
@ -3115,3 +3110,23 @@ async def test_partial_replication_on_same_source_master(df_factory, use_takeove
lines = replica2.find_in_logs(f"Started full with localhost:{replica1.port}") lines = replica2.find_in_logs(f"Started full with localhost:{replica1.port}")
assert len(lines) == 0 assert len(lines) == 0
assert len(replica1.find_in_logs("No partial sync due to diff")) > 0 assert len(replica1.find_in_logs("No partial sync due to diff")) > 0
async def test_replicate_hset_with_expiry(df_factory: DflyInstanceFactory):
master = df_factory.create(proactor_threads=2)
replica = df_factory.create(proactor_threads=2)
master.start()
replica.start()
cm = master.client()
await cm.execute_command("HSETEX key 86400 name 1234")
cr = replica.client()
await cr.execute_command(f"REPLICAOF localhost {master.port}")
await wait_available_async(cr)
result = await cr.hgetall("key")
assert "name" in result
assert result["name"] == "1234"