Verify: migrate from Hazelcast to KiviDB
Count what is in each structure on both sides. On KiviDB, DBSIZE and INFO keyspace give the total and how many keys expire; count one map's keys by prefix…
Count what is in each structure on both sides. On KiviDB, DBSIZE and
INFO keyspace give the total and how many keys expire; count one map's keys
by prefix with SCAN:
redis-cli -h kividb.internal -p 6380 -a "$KIVIDB_PASSWORD" INFO keyspace
redis-cli -h kividb.internal -p 6380 -a "$KIVIDB_PASSWORD" --scan --pattern 'customer:*' | wc -l
This program compares each map's size, then N random entries with their
values and expiry, and the sizes of the other structures. It reuses
Migrate.toHash, so build it with the copy program:
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.core.EntryView;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.nio.serialization.genericrecord.GenericRecord;
import model.LegacyOrder;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
import java.util.*;
// Compares counts and N random entries per IMap between Hazelcast and KiviDB, values and expiry.
public class Verify {
static int bad = 0;
public static void main(String[] args) {
String hzAddress = System.getenv().getOrDefault("HZ_ADDRESS", "127.0.0.1:5701");
String hzCluster = System.getenv().getOrDefault("HZ_CLUSTER", "dev");
String kividbUrl = System.getenv().getOrDefault("KIVIDB_URL", "redis://127.0.0.1:6380");
int n = Integer.parseInt(System.getenv().getOrDefault("SAMPLE", "500"));
ClientConfig cc = new ClientConfig();
cc.setClusterName(hzCluster);
cc.getNetworkConfig().addAddress(hzAddress);
HazelcastInstance hz = HazelcastClient.newHazelcastClient(cc);
try (JedisPooled kv = new JedisPooled(java.net.URI.create(kividbUrl))) {
checkMap(hz.getMap("customers"), kv, "customer:", n);
checkMap(hz.getMap("sessions"), kv, "session:", n);
checkMap(hz.getMap("stock"), kv, "stock:", n);
checkMap(hz.getMap("legacy"), kv, "order:", n);
same("multimap tags keys", hz.getMultiMap("tags").keySet().size(), countKeys(kv, "tags:*"));
same("queue jobs", hz.getQueue("jobs").size(), kv.llen("jobs"));
same("list events", hz.getList("events").size(), kv.llen("events"));
same("set admins", hz.getSet("admins").size(), kv.scard("admins"));
same("ringbuffer audit", hz.getRingbuffer("audit").size(), kv.xlen("audit"));
same("replicated map config", hz.getReplicatedMap("config").size(), kv.hlen("config"));
System.out.println("KiviDB DBSIZE " + kv.dbSize());
}
hz.shutdown();
System.out.println(bad == 0 ? "OK: no mismatches" : "FAILED: " + bad + " mismatches");
System.exit(bad == 0 ? 0 : 1);
}
static void checkMap(IMap<Object, Object> map, JedisPooled kv, String prefix, int n) {
same("map " + map.getName() + " size", map.size(), countKeys(kv, prefix + "*"));
List<Object> keys = new ArrayList<>(map.keySet());
Collections.shuffle(keys);
for (Object k : keys.subList(0, Math.min(n, keys.size()))) {
String key = prefix + k;
EntryView<Object, Object> ev = map.getEntryView(k);
if (ev == null) continue; // expired since keySet()
Object v = ev.getValue();
boolean ok;
if (v instanceof GenericRecord r) ok = Migrate.toHash(r).equals(kv.hgetAll(key));
else if (v instanceof LegacyOrder o) ok = Map.of("id", Long.toString(o.id), "sku", o.sku, "qty", Integer.toString(o.qty)).equals(kv.hgetAll(key));
else ok = v.toString().equals(kv.get(key));
if (!ok) { bad++; System.out.println("value differs: " + key); continue; }
long now = System.currentTimeMillis();
long pttl = kv.pttl(key);
boolean hzExpires = ev.getExpirationTime() != Long.MAX_VALUE;
if (hzExpires != (pttl >= 0) || (hzExpires && Math.abs((ev.getExpirationTime() - now) - pttl) > 5000)) {
bad++;
System.out.printf("expiry differs: %s hazelcast=%d kividb_pttl=%d%n", key, ev.getExpirationTime() - now, pttl);
}
}
System.out.printf("map %s: sampled %d%n", map.getName(), Math.min(n, keys.size()));
}
static long countKeys(JedisPooled kv, String pattern) {
long c = 0;
String cursor = ScanParams.SCAN_POINTER_START;
ScanParams p = new ScanParams().match(pattern).count(1000);
do {
ScanResult<String> r = kv.scan(cursor, p);
c += r.getResult().size();
cursor = r.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
return c;
}
static void same(String what, long hzCount, long kvCount) {
System.out.printf("%-28s hazelcast=%d kividb=%d%s%n", what, hzCount, kvCount, hzCount == kvCount ? "" : " MISMATCH");
if (hzCount != kvCount) bad++;
}
}
map.keySet() brings every key to the client; on very large maps, sample
from a few partitions' worth instead. Run it while writes are frozen, or
expect small differences in maps that are changing.