diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ace41d5a9..d4f8578180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ - [#833](https://github.com/crypto-org-chain/cronos/pull/833) Fix rollback command. +### Improvements + +- [#890](https://github.com/crypto-org-chain/cronos/pull/890) optimize memiavl snapshot format. + *Feb 09, 2022* ## v1.0.4 diff --git a/go.mod b/go.mod index cca2798ef7..648e6ca61c 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect + github.com/VictoriaMetrics/metrics v1.23.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect github.com/alitto/pond v1.8.2 // indirect github.com/allegro/bigcache v1.2.1 // indirect @@ -55,6 +56,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -129,6 +131,7 @@ require ( github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect + github.com/ledgerwatch/log/v3 v3.7.0 // indirect github.com/lib/pq v1.10.6 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -161,6 +164,7 @@ require ( github.com/rs/zerolog v1.27.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.14.0 // indirect @@ -171,12 +175,16 @@ require ( github.com/tidwall/btree v1.5.0 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect + github.com/torquem-ch/mdbx-go v0.27.5 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.10 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/histogram v1.2.0 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.24.0 // indirect + go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.4.0 // indirect diff --git a/go.sum b/go.sum index 07db37b4c7..6f1a36e242 100644 --- a/go.sum +++ b/go.sum @@ -227,6 +227,8 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDO github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/VictoriaMetrics/metrics v1.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0= +github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= @@ -317,6 +319,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -840,6 +844,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/ledgerwatch/log/v3 v3.7.0 h1:aFPEZdwZx4jzA3+/Pf8wNDN5tCI0cIolq/kfvgcM+og= +github.com/ledgerwatch/log/v3 v3.7.0/go.mod h1:J2Jl6zV/58LeA6LTaVVnCGyf1/cYYSEOOLHY4ZN8S2A= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -976,6 +982,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -1157,6 +1164,8 @@ github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZF github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/torquem-ch/mdbx-go v0.27.5 h1:bbhXQGFCmoxbRDXKYEJwxSOOTeBKwoD4pFBUpK9+V1g= +github.com/torquem-ch/mdbx-go v0.27.5/go.mod h1:T2fsoJDVppxfAPTLd1svUgH1kpPmeXdPESmroSHcL1E= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= @@ -1174,8 +1183,12 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -1213,6 +1226,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= @@ -1509,6 +1524,7 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/gomod2nix.toml b/gomod2nix.toml index f572238d01..0eebddabb8 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -40,6 +40,9 @@ schema = 3 [mod."github.com/VictoriaMetrics/fastcache"] version = "v1.6.0" hash = "sha256-u1dkRJ2Y5+hnYlkyMPm14HxKkAv999bjN622nZDjaBo=" + [mod."github.com/VictoriaMetrics/metrics"] + version = "v1.23.1" + hash = "sha256-z2X1DkTPXhI1eI9AJJ8d6LDeapiaKmd2l/2orxKsxi0=" [mod."github.com/Workiva/go-datastructures"] version = "v1.0.53" hash = "sha256-W6qOvqu8sokMlZrpOF1SWG138H0/BotywKNLlDF8Zug=" @@ -76,6 +79,9 @@ schema = 3 [mod."github.com/btcsuite/btcd/chaincfg/chainhash"] version = "v1.0.1" hash = "sha256-vix0j/KGNvoKjhlKgVeSLY6un2FHeIEoZWMC4z3yvZ4=" + [mod."github.com/c2h5oh/datasize"] + version = "v0.0.0-20220606134207-859f65c6625b" + hash = "sha256-1uH+D3w0Y/B3poXm545XGrT4S4c+msTbj7gKgu9pbPM=" [mod."github.com/cenkalti/backoff/v4"] version = "v4.1.3" hash = "sha256-u6MEDopHoTWAZoVvvXOKnAg++xre53YgQx0gmf6t2KU=" @@ -327,6 +333,9 @@ schema = 3 [mod."github.com/ledgerwatch/erigon-lib"] version = "v0.0.0-20230210071639-db0e7ed11263" hash = "sha256-SKFGLsJV6G4xIQ5IU+qq9EY3v0/I8B/CTMcDOhXGfc4=" + [mod."github.com/ledgerwatch/log/v3"] + version = "v3.7.0" + hash = "sha256-o0tOdlRL0LSU3BRJxK15LkBEh/RDGnsTWWVU/vgmARk=" [mod."github.com/lib/pq"] version = "v1.10.6" hash = "sha256-8EhFwY/9YH5L/fd6l2beOnC3VvpegRAmCCsnDVJBqBM=" @@ -337,18 +346,6 @@ schema = 3 version = "v1.7.15-0.20230222024938-b61261a9193b" hash = "sha256-Hry5mpO8WqCuYZ0zCnOt6kopDGprc7/nI318A2D+Kk0=" replaced = "github.com/linxGnu/grocksdb" - [mod."github.com/lispad/go-generics-tools"] - version = "v1.1.0" - hash = "sha256-MPeqIrDVGv6d/mBK4o48eaRc8Qga5l9i1tpUyrgMoII=" - [mod."github.com/lucasjones/reggen"] - version = "v0.0.0-20180717132126-cdb49ff09d77" - hash = "sha256-gX55KvGzJkyghKIhmQFnwCI9mXYt/FtDgI/sQ4xNfrE=" - [mod."github.com/lufeee/execinquery"] - version = "v1.2.1" - hash = "sha256-Hg+/0StXgoflSxwiw96IYhYybYy26o1QA66a5pMsswo=" - [mod."github.com/lyft/protoc-gen-validate"] - version = "v0.0.13" - hash = "sha256-JhFMmEaP1amtJJBLWFVqjjHeHuAHRP0qwLMMFX2b3FM=" [mod."github.com/magiconair/properties"] version = "v1.8.6" hash = "sha256-xToSfpuePctkTdhJtsuKIEkXwfMZbnkFT98ahIfd4wY=" @@ -446,6 +443,9 @@ schema = 3 [mod."github.com/shirou/gopsutil"] version = "v3.21.4-0.20210419000835-c7a38de76ee5+incompatible" hash = "sha256-oqIqyFquWabIE6DID6uTEc8oFEmM1rVu2ATn3toiCEg=" + [mod."github.com/spaolacci/murmur3"] + version = "v1.1.0" + hash = "sha256-RWD4PPrlAsZZ8Xy356MBxpj+/NZI7w2XOU14Ob7/Y9M=" [mod."github.com/spf13/afero"] version = "v1.9.2" hash = "sha256-R1mir7Fu95QK+YL99U14RGbLJzxqWRH5rSFpssgJvzA=" @@ -496,12 +496,21 @@ schema = 3 [mod."github.com/tklauser/numcpus"] version = "v0.4.0" hash = "sha256-ndE82nOb3agubhEV7aRzEqqTlN4DPbKFHEm2+XZLn8k=" + [mod."github.com/torquem-ch/mdbx-go"] + version = "v0.27.5" + hash = "sha256-GSDtoGSdX8E4QLnBgLzWVW/9qYMuvcsogoo2LC8T3eU=" [mod."github.com/tyler-smith/go-bip39"] version = "v1.1.0" hash = "sha256-3YhWBtSwRLGwm7vNwqumphZG3uLBW1vwT9QkQ8JuSjU=" [mod."github.com/ulikunitz/xz"] version = "v0.5.10" hash = "sha256-bogOwQNmQVS7W+C7wci7XEUeYm9TB7PnxnyBIXKYbm0=" + [mod."github.com/valyala/fastrand"] + version = "v1.1.0" + hash = "sha256-+tvsaq1TJGA/bCLXztr0iIZ08CatTG9x7ooNTPIKSZY=" + [mod."github.com/valyala/histogram"] + version = "v1.2.0" + hash = "sha256-zmCr5jZHdbOes9XiAA8HdXBHBeDiaOYVWHeW8tQIbUQ=" [mod."github.com/zondax/hid"] version = "v0.9.0" hash = "sha256-PvXtxXo/3C+DS9ZeGBlr4zXbIpaYNtMqLzxYhusFXNY=" @@ -515,6 +524,9 @@ schema = 3 [mod."go.opencensus.io"] version = "v0.24.0" hash = "sha256-4H+mGZgG2c9I1y0m8avF4qmt8LUKxxVsTqR8mKgP4yo=" + [mod."go.uber.org/atomic"] + version = "v1.10.0" + hash = "sha256-E6UEDc1eh/cLUFd+J86cDesQ0B8wEv/DdaAVKb+x2t8=" [mod."golang.org/x/crypto"] version = "v0.6.0" hash = "sha256-rtJfWOcpCk+DUskDsOZBt9BU/pHjHJ60LkF4VOKCAI8=" diff --git a/memiavl/README.md b/memiavl/README.md index 7f4b13bd0d..4b76584376 100644 --- a/memiavl/README.md +++ b/memiavl/README.md @@ -10,6 +10,10 @@ * 27 Jan 2023: * Update metadata file format * Encode key length with 4 bytes instead of 2. +* 24 Feb 2023: + * Reduce node size without hash) from 32bytes to 16bytes, leverage properties of post-order traversal. + * Merge key-values into single kvs file, build MPHF hash table to index it. + ## The Journey @@ -38,10 +42,10 @@ It also integrates well with versiondb, because versiondb can also be derived fr ### Change Set File ``` -version: int64 -size: int64 // size of whole payload +version: 8 +size: 8 // size of whole payload payload: - delete: int8 + delete: 1 keyLen: varint-uint64 key [ // if delete is false @@ -63,49 +67,55 @@ IAVL snapshot is composed by four files: - `metadata`, 16bytes: ``` - magic: uint32 - format: uint32 - version: uint64 - root node index: uint32 + magic: 4 + format: 4 + version: 4 + root node index: 4 ``` -- `nodes`, array of fixed size(64bytes) nodes, the node format is like this: +- `nodes`, array of fixed size(16+32bytes) nodes, the node format is like this: ``` - height : uint8 // padded to 4bytes - version : uint32 - size : uint64 - key : uint64 // offset in keys file - left : uint32 // inner node only - right : uint32 // inner node only - value : uint64 offset // offset in values file, leaf node only - hash : [32]byte + # branch + height : 1 + _padding : 3 + version : 4 + size : 4 + key node : 4 + hash : [32]byte + + # leaf + height : 1 + _padding : 3 + version : 4 + key offset : 8 ``` - The node has fixed length, can be indexed directly. The nodes reference each other with the index, nodes are written in post-order, so the root node is always placed at the end. - - Some integers are using `uint32`, should be enough in forseeable future, but could be changed to `uint64` to be safer. + The node has fixed length, can be indexed directly. The nodes reference each other with the node index, nodes are written in post-order, so the root node is always placed at the end. - The implementation will read the mmap-ed content in a zero-copy way, won't use extra node cache, it will only rely on the OS page cache. + For branch node, the `key node` field reference the smallest leaf node in the right branch, the key slice is fetched from there indirectly, the leaf nodes will store key slice and value index informations, but the version field is stored in `keys` file instead. -- `keys`, sequence of length prefixed leaf node keys, ordered and no duplication. + The branch node's left/child node indexes are inferenced from existing information and properties of post-order traversal: ``` - size: uint32 - payload - *repeat* + right child index = self index - 1 + left child index = key node - 1 ``` - Key size is encoded in `uint32`, so the maximum key length supported is `1<<32-1`, around 4G. + The version/size/node indexes are encoded with 4 bytes, should be enough in foreseeable future, but could be changed to more bytes in the future. -- `values`, sequence of length prefixed leaf node values. + The implementation will read the mmap-ed content in a zero-copy way, won't use extra node cache, it will only rely on the OS page cache. + +- `kvs`, sequence of leaf node key-value pairs, the keys are ordered and no duplication. ``` - size: uint32 - payload + keyLen: varint-uint64 + key + valueLen: varint-uint64 + value *repeat* ``` - Value size is encoded in `uint32`, so maximum value length supported is `1<<32-1`, around 4G. +- `kvs.index`, Minimal-perfect-hash-function build from `kvs`, support query as a hash map. #### Compression @@ -115,4 +125,6 @@ The items in snapshot reference with each other by file offsets, we can apply so [VersionDB](../README.md) is to support query and iterating historical versions of key-values pairs, currently implemented with rocksdb's experimental user-defined timestamp feature, support query and iterate key-value pairs by version, it's an alternative way to support grpc query service, and much more compact than IAVL trees, similar in size with the compressed change set files. -[^1]: https://github.com/facebook/zstd/blob/dev/contrib/seekable_format/zstd_seekable_compression_format.md \ No newline at end of file +After versiondb is fully integrated, IAVL tree don't need to serve queries at all, it don't need to store the values at all, just store the value hashes would be enough. + +[^1]: https://github.com/facebook/zstd/blob/dev/contrib/seekable_format/zstd_seekable_compression_format.md diff --git a/memiavl/benchmark_test.go b/memiavl/benchmark_test.go new file mode 100644 index 0000000000..5e10d33151 --- /dev/null +++ b/memiavl/benchmark_test.go @@ -0,0 +1,159 @@ +package memiavl + +import ( + "bytes" + "encoding/binary" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tidwall/btree" +) + +func BenchmarkByteCompare(b *testing.B) { + var x, y [32]byte + for i := 0; i < b.N; i++ { + _ = bytes.Compare(x[:], y[:]) + } +} + +func BenchmarkRandomGet(b *testing.B) { + items := genRandItems(1000000) + targetKey := items[500].key + targetValue := items[500].value + targetItem := itemT{key: targetKey} + + tree := New() + for _, item := range items { + tree.Set(item.key, item.value) + } + + bt2 := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 2, + }) + for _, item := range items { + bt2.Set(item) + } + + bt32 := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 32, + }) + for _, item := range items { + bt32.Set(item) + } + + snapshotDir := b.TempDir() + err := tree.WriteSnapshot(snapshotDir) + require.NoError(b, err) + snapshot, err := OpenSnapshot(snapshotDir) + require.NoError(b, err) + defer snapshot.Close() + diskTree := NewFromSnapshot(snapshot) + + require.Equal(b, targetValue, tree.Get(targetKey)) + require.Equal(b, targetValue, diskTree.Get(targetKey)) + require.Equal(b, targetValue, snapshot.Get(targetKey)) + v, _ := bt2.Get(targetItem) + require.Equal(b, targetValue, v.value) + v, _ = bt32.Get(targetItem) + require.Equal(b, targetValue, v.value) + + b.ResetTimer() + b.Run("memiavl", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = tree.Get(targetKey) + } + }) + b.Run("memiavl-disk", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = diskTree.Get(targetKey) + } + }) + b.Run("snapshot-get", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = snapshot.Get(targetKey) + } + }) + b.Run("btree-degree-2", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = bt2.Get(targetItem) + } + }) + b.Run("btree-degree-32", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = bt32.Get(targetItem) + } + }) +} + +func BenchmarkRandomSet(b *testing.B) { + items := genRandItems(1000000) + b.ResetTimer() + b.Run("memiavl", func(b *testing.B) { + for i := 0; i < b.N; i++ { + tree := New() + for _, item := range items { + tree.Set(item.key, item.value) + } + } + }) + b.Run("tree2", func(b *testing.B) { + for i := 0; i < b.N; i++ { + bt := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 2, + }) + for _, item := range items { + bt.Set(item) + } + } + }) + b.Run("tree32", func(b *testing.B) { + for i := 0; i < b.N; i++ { + bt := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 32, + }) + for _, item := range items { + bt.Set(item) + } + } + }) +} + +type itemT struct { + key, value []byte +} + +func lessG(a, b itemT) bool { + return bytes.Compare(a.key, b.key) == -1 +} + +func int64ToItemT(n uint64) itemT { + var key, value [8]byte + binary.BigEndian.PutUint64(key[:], n) + binary.LittleEndian.PutUint64(value[:], n) + return itemT{ + key: key[:], + value: value[:], + } +} + +func genRandItems(n int) []itemT { + r := rand.New(rand.NewSource(0)) + items := make([]itemT, n) + itemsM := make(map[uint64]bool) + for i := 0; i < n; i++ { + for { + key := uint64(r.Int63n(10000000000000000)) + if !itemsM[key] { + itemsM[key] = true + items[i] = int64ToItemT(key) + break + } + } + } + return items +} diff --git a/memiavl/go.mod b/memiavl/go.mod index 6f5c102ad1..724e192eb6 100644 --- a/memiavl/go.mod +++ b/memiavl/go.mod @@ -8,10 +8,13 @@ require ( github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 github.com/stretchr/testify v1.8.1 github.com/tendermint/tm-db v0.6.7 + github.com/tidwall/btree v1.5.0 ) require ( github.com/DataDog/zstd v1.4.1 // indirect + github.com/VictoriaMetrics/metrics v1.23.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/confio/ics23/go v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -19,19 +22,29 @@ require ( github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect + github.com/ledgerwatch/log/v3 v3.7.0 // indirect github.com/linxGnu/grocksdb v1.7.10 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tendermint/tendermint v0.34.20 // indirect + github.com/torquem-ch/mdbx-go v0.27.5 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/histogram v1.2.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect + go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.6.0 // indirect + golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect diff --git a/memiavl/go.sum b/memiavl/go.sum index 4de116099e..0d86414853 100644 --- a/memiavl/go.sum +++ b/memiavl/go.sum @@ -4,7 +4,11 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/VictoriaMetrics/metrics v1.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0= +github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -38,6 +42,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -87,9 +93,16 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/ledgerwatch/log/v3 v3.7.0 h1:aFPEZdwZx4jzA3+/Pf8wNDN5tCI0cIolq/kfvgcM+og= +github.com/ledgerwatch/log/v3 v3.7.0/go.mod h1:J2Jl6zV/58LeA6LTaVVnCGyf1/cYYSEOOLHY4ZN8S2A= github.com/linxGnu/grocksdb v1.7.10 h1:dz7RY7GnFUA+GJO6jodyxgkUeGMEkPp3ikt9hAcNGEw= github.com/linxGnu/grocksdb v1.7.10/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -101,6 +114,7 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -133,12 +147,22 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ= +github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +github.com/torquem-ch/mdbx-go v0.27.5 h1:bbhXQGFCmoxbRDXKYEJwxSOOTeBKwoD4pFBUpK9+V1g= +github.com/torquem-ch/mdbx-go v0.27.5/go.mod h1:T2fsoJDVppxfAPTLd1svUgH1kpPmeXdPESmroSHcL1E= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -147,6 +171,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -172,6 +198,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -186,6 +213,8 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/memiavl/layout_little_endian.go b/memiavl/layout_little_endian.go new file mode 100644 index 0000000000..f89dce3c30 --- /dev/null +++ b/memiavl/layout_little_endian.go @@ -0,0 +1,50 @@ +//go:build !nativebyteorder +// +build !nativebyteorder + +package memiavl + +import ( + "encoding/binary" +) + +// Nodes is a continuously stored IAVL nodes +type Nodes struct { + data []byte +} + +func NewNodes(data []byte) (Nodes, error) { + return Nodes{data}, nil +} + +func (nodes Nodes) Node(i uint32) *NodeLayout { + return &NodeLayout{data: nodes.data[i*SizeNode:]} +} + +// see comment of `PersistedNode` +type NodeLayout struct { + data []byte +} + +func (node *NodeLayout) Height() uint8 { + return node.data[OffsetHeight] +} + +func (node *NodeLayout) Version() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetVersion : OffsetVersion+4]) +} + +func (node *NodeLayout) Size() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetSize : OffsetSize+4]) +} + +func (node *NodeLayout) KeyOffset() uint64 { + return binary.LittleEndian.Uint64(node.data[OffsetKeyOffset : OffsetKeyOffset+8]) +} + +func (node *NodeLayout) KeyNode() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetKeyNode : OffsetKeyNode+4]) +} + +func (node *NodeLayout) Hash() []byte { + return node.data[OffsetHash : OffsetHash+SizeHash] +} diff --git a/memiavl/layout_native.go b/memiavl/layout_native.go new file mode 100644 index 0000000000..4f6ae03422 --- /dev/null +++ b/memiavl/layout_native.go @@ -0,0 +1,65 @@ +//go:build nativebyteorder +// +build nativebyteorder + +package memiavl + +import ( + "errors" + "unsafe" +) + +// Nodes is a continuously stored IAVL nodes +type Nodes struct { + nodes []NodeLayout +} + +func NewNodes(buf []byte) (Nodes, error) { + // check alignment and size of the buffer + p := unsafe.Pointer(unsafe.SliceData(buf)) + if uintptr(p)%unsafe.Alignof(NodeLayout{}) != 0 { + return Nodes{}, errors.New("input buffer is not aligned") + } + size := int(unsafe.Sizeof(NodeLayout{})) + if len(buf)%size != 0 { + return Nodes{}, errors.New("input buffer length is not correct") + } + nodes := unsafe.Slice((*NodeLayout)(p), len(buf)/size) + return Nodes{nodes}, nil +} + +func (nodes Nodes) Node(i uint32) *NodeLayout { + return &nodes.nodes[i] +} + +// see comment of `PersistedNode` +type NodeLayout struct { + data [4]uint32 + hash [32]byte +} + +func (node *NodeLayout) Height() uint8 { + return uint8(node.data[0]) +} + +func (node *NodeLayout) Version() uint32 { + return node.data[1] +} + +func (node *NodeLayout) Size() uint32 { + if node.Height() == 0 { + return 1 + } + return node.data[2] +} + +func (node *NodeLayout) KeyNode() uint32 { + return node.data[3] +} + +func (node *NodeLayout) KeyOffset() uint64 { + return uint64(node.data[2]) | uint64(node.data[3])<<32 +} + +func (node *NodeLayout) Hash() []byte { + return node.hash[:] +} diff --git a/memiavl/mem_node.go b/memiavl/mem_node.go index b0ba340e0e..92473f8840 100644 --- a/memiavl/mem_node.go +++ b/memiavl/mem_node.go @@ -1,14 +1,15 @@ package memiavl import ( + "bytes" "encoding/binary" "io" ) type MemNode struct { - height int8 + height uint8 size int64 - version int64 + version uint32 key []byte value []byte left Node @@ -19,7 +20,7 @@ type MemNode struct { var _ Node = (*MemNode)(nil) -func newLeafNode(key, value []byte, version int64) *MemNode { +func newLeafNode(key, value []byte, version uint32) *MemNode { return &MemNode{ key: key, value: value, version: version, size: 1, } @@ -29,7 +30,7 @@ func (node *MemNode) isLeaf() bool { return node.height == 0 } -func (node *MemNode) Height() int8 { +func (node *MemNode) Height() uint8 { return node.height } @@ -37,7 +38,7 @@ func (node *MemNode) Size() int64 { return node.size } -func (node *MemNode) Version() int64 { +func (node *MemNode) Version() uint32 { return node.version } @@ -58,7 +59,7 @@ func (node *MemNode) Right() Node { } // Mutate clears hash and update version field to prepare for further modifications. -func (node *MemNode) Mutate(version int64) *MemNode { +func (node *MemNode) Mutate(version uint32) *MemNode { node.version = version node.hash = nil return node @@ -78,7 +79,7 @@ func (node *MemNode) Hash() []byte { } func (node *MemNode) updateHeightSize() { - node.height = maxInt8(node.left.Height(), node.right.Height()) + 1 + node.height = maxUInt8(node.left.Height(), node.right.Height()) + 1 node.size = node.left.Size() + node.right.Size() } @@ -97,7 +98,7 @@ func calcBalance(node Node) int { // L S // / \ / \ // LR LR -func (node *MemNode) rotateRight(version int64) *MemNode { +func (node *MemNode) rotateRight(version uint32) *MemNode { newSelf := node.left.Mutate(version) node.left = node.left.Right() newSelf.right = node @@ -113,7 +114,7 @@ func (node *MemNode) rotateRight(version int64) *MemNode { // R S // / \ / \ // RL RL -func (node *MemNode) rotateLeft(version int64) *MemNode { +func (node *MemNode) rotateLeft(version uint32) *MemNode { newSelf := node.right.Mutate(version) node.right = node.right.Left() newSelf.left = node @@ -123,7 +124,7 @@ func (node *MemNode) rotateLeft(version int64) *MemNode { } // Invariant: node is returned by `Mutate(version)`. -func (node *MemNode) reBalance(version int64) *MemNode { +func (node *MemNode) reBalance(version uint32) *MemNode { balance := node.calcBalance() switch { case balance > 1: @@ -150,6 +151,20 @@ func (node *MemNode) reBalance(version int64) *MemNode { } } +func (node *MemNode) Get(key []byte) []byte { + if node.isLeaf() { + if bytes.Equal(key, node.key) { + return node.value + } + return nil + } + + if bytes.Compare(key, node.key) == -1 { + return node.Left().Get(key) + } + return node.Right().Get(key) +} + // EncodeBytes writes a varint length-prefixed byte slice to the writer, // it's used for hash computation, must be compactible with the official IAVL implementation. func EncodeBytes(w io.Writer, bz []byte) error { @@ -162,7 +177,7 @@ func EncodeBytes(w io.Writer, bz []byte) error { return err } -func maxInt8(a, b int8) int8 { +func maxUInt8(a, b uint8) uint8 { if a > b { return a } diff --git a/memiavl/node.go b/memiavl/node.go index 57d27d07cc..04c23cb520 100644 --- a/memiavl/node.go +++ b/memiavl/node.go @@ -10,9 +10,9 @@ import ( // Node interface encapsulate the interface of both PersistedNode and MemNode. type Node interface { - Height() int8 + Height() uint8 Size() int64 - Version() int64 + Version() uint32 Key() []byte Value() []byte Left() Node @@ -20,7 +20,10 @@ type Node interface { Hash() []byte // PersistedNode clone a new node, MemNode modify in place - Mutate(version int64) *MemNode + Mutate(version uint32) *MemNode + + // Get query the value for a key, it's put into interface because a specialized implementation is more efficient. + Get(key []byte) []byte } func isLeaf(node Node) bool { @@ -30,7 +33,7 @@ func isLeaf(node Node) bool { // setRecursive do set operation. // it always do modification and return new `MemNode`, even if the value is the same. // also returns if it's an update or insertion, if update, the tree height and balance is not changed. -func setRecursive(node Node, key, value []byte, version int64) (*MemNode, bool) { +func setRecursive(node Node, key, value []byte, version uint32) (*MemNode, bool) { if node == nil { return newLeafNode(key, value, version), true } @@ -89,7 +92,7 @@ func setRecursive(node Node, key, value []byte, version int64) (*MemNode, bool) // - (nil, origNode, nil) -> nothing changed in subtree // - (value, nil, newKey) -> leaf node is removed // - (value, new node, newKey) -> subtree changed -func removeRecursive(node Node, key []byte, version int64) ([]byte, Node, []byte) { +func removeRecursive(node Node, key []byte, version uint32) ([]byte, Node, []byte) { if node == nil { return nil, nil, nil } @@ -148,7 +151,7 @@ func writeHashBytes(node Node, w io.Writer) error { if _, err := w.Write(buf[0:n]); err != nil { return fmt.Errorf("writing size, %w", err) } - n = binary.PutVarint(buf[:], node.Version()) + n = binary.PutVarint(buf[:], int64(node.Version())) if _, err := w.Write(buf[0:n]); err != nil { return fmt.Errorf("writing version, %w", err) } diff --git a/memiavl/persisted_node.go b/memiavl/persisted_node.go index 7ef99cd2e9..9b90160fb5 100644 --- a/memiavl/persisted_node.go +++ b/memiavl/persisted_node.go @@ -1,94 +1,101 @@ package memiavl import ( + "bytes" "crypto/sha256" - "encoding/binary" ) const ( OffsetHeight = 0 OffsetVersion = OffsetHeight + 4 OffsetSize = OffsetVersion + 4 - OffsetKey = OffsetSize + 8 - OffsetRight = OffsetKey + 8 - OffsetLeft = OffsetRight + 4 - OffsetValue = OffsetKey + 8 - OffsetHash = OffsetValue + 8 + OffsetKeyNode = OffsetSize + 4 + // leaf node repurpose two uint32 to store the offset in kv file. + OffsetKeyOffset = OffsetVersion + 4 + + OffsetHash = OffsetKeyNode + 4 SizeHash = sha256.Size SizeNodeWithoutHash = OffsetHash SizeNode = SizeNodeWithoutHash + SizeHash - - // encoding key/value length as 4 bytes with little endianness. - SizeKeyLen = 4 - SizeValueLen = 4 ) // PersistedNode is backed by serialized byte array, usually mmap-ed from disk file. // Encoding format (all integers are encoded in little endian): -// - height : int8 // padded to 4bytes -// - version : int32 -// - size : int64 -// - key : int64 -// - left : int32 // node index, inner node only -// - right : int32 // node index, inner node only -// - value : int64 offset // leaf node only -// - hash : [32]byte +// +// Branch node: +// - height : 1 +// - _padding : 3 +// - version : 4 +// - size : 4 +// - key node : 4 // node index of the smallest leaf in right branch +// - hash : 32 +// Leaf node: +// - height : 1 +// - _padding : 3 +// - version : 4 +// - key offset : 8 +// - hash : 32 type PersistedNode struct { snapshot *Snapshot - offset uint64 + index uint32 } var _ Node = PersistedNode{} -func (node PersistedNode) Height() int8 { - return int8(node.snapshot.nodes[node.offset+OffsetHeight]) +func (node PersistedNode) data() *NodeLayout { + return node.snapshot.nodesLayout.Node(node.index) +} + +func (node PersistedNode) Height() uint8 { + return node.data().Height() } -func (node PersistedNode) Version() int64 { - return int64(binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetVersion:])) +func (node PersistedNode) Version() uint32 { + return node.data().Version() } func (node PersistedNode) Size() int64 { - return int64(binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetSize:])) + data := node.data() + if data.Height() == 0 { + return 1 + } + return int64(data.Size()) } func (node PersistedNode) Key() []byte { - keyOffset := binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetKey:]) - keyLen := uint64(binary.LittleEndian.Uint32(node.snapshot.keys[keyOffset:])) - keyOffset += SizeKeyLen - return node.snapshot.keys[keyOffset : keyOffset+keyLen] + data := node.data() + if data.Height() != 0 { + data = node.snapshot.nodesLayout.Node(data.KeyNode()) + } + return node.snapshot.Key(data.KeyOffset()) } // Value result is not defined for non-leaf node. func (node PersistedNode) Value() []byte { - valueOffset := binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetValue:]) - valueLen := uint64(binary.LittleEndian.Uint32(node.snapshot.values[valueOffset:])) - valueOffset += SizeValueLen - return node.snapshot.values[valueOffset : valueOffset+valueLen] + _, value := node.snapshot.KeyValue(node.data().KeyOffset()) + return value } // Left result is not defined for leaf nodes. func (node PersistedNode) Left() Node { - nodeIndex := binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetLeft:]) - return PersistedNode{snapshot: node.snapshot, offset: uint64(nodeIndex) * SizeNode} + return PersistedNode{snapshot: node.snapshot, index: node.data().KeyNode() - 1} } // Right result is not defined for leaf nodes. func (node PersistedNode) Right() Node { - nodeIndex := binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetRight:]) - return PersistedNode{snapshot: node.snapshot, offset: uint64(nodeIndex) * SizeNode} + return PersistedNode{snapshot: node.snapshot, index: node.index - 1} } func (node PersistedNode) Hash() []byte { - offset := node.offset + OffsetHash - return node.snapshot.nodes[offset : offset+SizeHash] + return node.data().Hash() } -func (node PersistedNode) Mutate(version int64) *MemNode { +func (node PersistedNode) Mutate(version uint32) *MemNode { + data := node.data() mnode := &MemNode{ - height: node.Height(), - size: node.Size(), + height: data.Height(), + size: int64(data.Size()), version: version, key: node.Key(), } @@ -100,3 +107,33 @@ func (node PersistedNode) Mutate(version int64) *MemNode { } return mnode } + +func (node PersistedNode) Get(key []byte) []byte { + return getPersistedNode(node.snapshot, node.index, key) +} + +// getPersistedNode specialize the get function for `PersistedNode`. +func getPersistedNode(snapshot *Snapshot, index uint32, key []byte) []byte { + nodes := snapshot.nodesLayout + + for { + node := nodes.Node(index) + if node.Height() == 0 { + nodeKey, value := snapshot.KeyValue(node.KeyOffset()) + if bytes.Equal(key, nodeKey) { + return value + } + return nil + } + + keyNode := node.KeyNode() + nodeKey := snapshot.Key(nodes.Node(keyNode).KeyOffset()) + if bytes.Compare(key, nodeKey) == -1 { + // left child + index = keyNode - 1 + } else { + // right child + index-- + } + } +} diff --git a/memiavl/snapshot.go b/memiavl/snapshot.go index d554db226c..270c9d6171 100644 --- a/memiavl/snapshot.go +++ b/memiavl/snapshot.go @@ -2,6 +2,7 @@ package memiavl import ( "bufio" + "bytes" "encoding/binary" "fmt" "io" @@ -10,7 +11,9 @@ import ( "path/filepath" "github.com/hashicorp/go-multierror" + "github.com/ledgerwatch/erigon-lib/etl" "github.com/ledgerwatch/erigon-lib/mmap" + "github.com/ledgerwatch/erigon-lib/recsplit" ) const ( @@ -20,30 +23,40 @@ const ( // the initial snapshot format SnapshotFormat = 0 - // magic: uint32, format: uint32, version: uint64, root node index: uint32 - SizeMetadata = 20 + // magic: uint32, format: uint32, version: uint32, root node index: uint32 + SizeMetadata = 16 // EmptyRootNodeIndex is a special value of root node index to represent empty tree EmptyRootNodeIndex = math.MaxUint32 + + FileNameNodes = "nodes" + FileNameKVs = "kvs" + FileNameKVIndex = "kvs.index" + FileNameMetadata = "metadata" ) // Snapshot manage the lifecycle of mmap-ed files for the snapshot, // it must out live the objects that derived from it. type Snapshot struct { - nodesMap *MmapFile - keysMap *MmapFile - valuesMap *MmapFile + nodesMap *MmapFile + kvsMap *MmapFile + + nodes []byte + kvs []byte - nodes []byte - keys []byte - values []byte + // hash index of kvs + index *recsplit.Index + indexReader *recsplit.IndexReader // reader for the index // parsed from metadata file - version uint64 + version uint32 rootIndex uint32 + + // wrapping the raw nodes buffer + nodesLayout Nodes } -func NewEmptySnapshot(version uint64) *Snapshot { +func NewEmptySnapshot(version uint32) *Snapshot { return &Snapshot{ version: version, rootIndex: EmptyRootNodeIndex, @@ -54,7 +67,7 @@ func NewEmptySnapshot(version uint64) *Snapshot { // and mmap the other files. func OpenSnapshot(snapshotDir string) (*Snapshot, error) { // read metadata file - bz, err := os.ReadFile(filepath.Join(snapshotDir, "metadata")) + bz, err := os.ReadFile(filepath.Join(snapshotDir, FileNameMetadata)) if err != nil { return nil, err } @@ -70,45 +83,38 @@ func OpenSnapshot(snapshotDir string) (*Snapshot, error) { if format != SnapshotFormat { return nil, fmt.Errorf("unknown snapshot format: %d", format) } - version := binary.LittleEndian.Uint64(bz[8:]) - rootIndex := binary.LittleEndian.Uint32(bz[16:]) + version := binary.LittleEndian.Uint32(bz[8:]) + rootIndex := binary.LittleEndian.Uint32(bz[12:]) if rootIndex == EmptyRootNodeIndex { // we can't mmap empty files, so have to return early return NewEmptySnapshot(version), nil } - var nodesMap, keysMap, valuesMap *MmapFile + var nodesMap, kvsMap *MmapFile cleanupHandles := func(err error) error { if nodesMap != nil { if merr := nodesMap.Close(); merr != nil { err = multierror.Append(merr, err) } } - if keysMap != nil { - if merr := keysMap.Close(); merr != nil { - err = multierror.Append(merr, err) - } - } - if valuesMap != nil { - if merr := valuesMap.Close(); merr != nil { + if kvsMap != nil { + if merr := kvsMap.Close(); merr != nil { err = multierror.Append(merr, err) } } return err } - if nodesMap, err = NewMmap(filepath.Join(snapshotDir, "nodes")); err != nil { + if nodesMap, err = NewMmap(filepath.Join(snapshotDir, FileNameNodes)); err != nil { return nil, cleanupHandles(err) } - if keysMap, err = NewMmap(filepath.Join(snapshotDir, "keys")); err != nil { - return nil, cleanupHandles(err) - } - if valuesMap, err = NewMmap(filepath.Join(snapshotDir, "values")); err != nil { + if kvsMap, err = NewMmap(filepath.Join(snapshotDir, FileNameKVs)); err != nil { return nil, cleanupHandles(err) } nodes := nodesMap.Data() + kvs := kvsMap.Data() if len(nodes) == 0 && rootIndex != 0 { return nil, cleanupHandles( @@ -122,18 +128,31 @@ func OpenSnapshot(snapshotDir string) (*Snapshot, error) { ) } + index, err := recsplit.OpenIndex(filepath.Join(snapshotDir, FileNameKVIndex)) + if err != nil { + return nil, cleanupHandles(err) + } + + nodesData, err := NewNodes(nodes) + if err != nil { + return nil, err + } + return &Snapshot{ - nodesMap: nodesMap, - keysMap: keysMap, - valuesMap: valuesMap, + nodesMap: nodesMap, + kvsMap: kvsMap, // cache the pointers - nodes: nodesMap.Data(), - keys: keysMap.Data(), - values: valuesMap.Data(), + nodes: nodes, + kvs: kvs, + + index: index, + indexReader: recsplit.NewIndexReader(index), version: version, rootIndex: rootIndex, + + nodesLayout: nodesData, }, nil } @@ -146,13 +165,14 @@ func (snapshot *Snapshot) Close() error { err = multierror.Append(err, merr) } } - if snapshot.keysMap != nil { - if merr := snapshot.keysMap.Close(); merr != nil { + if snapshot.kvsMap != nil { + if merr := snapshot.kvsMap.Close(); merr != nil { err = multierror.Append(err, merr) } } - if snapshot.valuesMap != nil { - if merr := snapshot.valuesMap.Close(); merr != nil { + + if snapshot.index != nil { + if merr := snapshot.index.Close(); merr != nil { err = multierror.Append(err, merr) } } @@ -171,12 +191,12 @@ func (snapshot *Snapshot) IsEmpty() bool { func (snapshot *Snapshot) Node(index uint32) PersistedNode { return PersistedNode{ snapshot: snapshot, - offset: uint64(index) * SizeNode, + index: index, } } // Version returns the version of the snapshot -func (snapshot *Snapshot) Version() uint64 { +func (snapshot *Snapshot) Version() uint32 { return snapshot.version } @@ -203,15 +223,44 @@ func (snapshot *Snapshot) ScanNodes(callback func(node PersistedNode) error) err return nil } +// Get lookup the value for the key through the hash index +func (snapshot *Snapshot) Get(key []byte) []byte { + offset := snapshot.indexReader.Lookup(key) + candidate, value := snapshot.KeyValue(offset) + if bytes.Equal(key, candidate) { + return value + } + return nil +} + +// Key returns a zero-copy slice of key by offset +func (snapshot *Snapshot) Key(offset uint64) []byte { + keyLen := binary.LittleEndian.Uint32(snapshot.kvs[offset:]) + offset += 4 + return snapshot.kvs[offset : offset+uint64(keyLen)] +} + +// KeyValue returns a zero-copy slice of key/value pair by offset +func (snapshot *Snapshot) KeyValue(offset uint64) ([]byte, []byte) { + len := uint64(binary.LittleEndian.Uint32(snapshot.kvs[offset:])) + offset += 4 + key := snapshot.kvs[offset : offset+len] + offset += len + len = uint64(binary.LittleEndian.Uint32(snapshot.kvs[offset:])) + offset += 4 + value := snapshot.kvs[offset : offset+len] + return key, value +} + // WriteSnapshot save the IAVL tree to a new snapshot directory. func (t *Tree) WriteSnapshot(snapshotDir string) (returnErr error) { var rootIndex uint32 if t.root == nil { rootIndex = EmptyRootNodeIndex } else { - nodesFile := filepath.Join(snapshotDir, "nodes") - keysFile := filepath.Join(snapshotDir, "keys") - valuesFile := filepath.Join(snapshotDir, "values") + nodesFile := filepath.Join(snapshotDir, FileNameNodes) + kvsFile := filepath.Join(snapshotDir, FileNameKVs) + kvsIndexFile := filepath.Join(snapshotDir, FileNameKVIndex) fpNodes, err := createFile(nodesFile) if err != nil { @@ -223,31 +272,21 @@ func (t *Tree) WriteSnapshot(snapshotDir string) (returnErr error) { } }() - fpKeys, err := createFile(keysFile) + fpKVs, err := createFile(kvsFile) if err != nil { return err } defer func() { - if err := fpKeys.Close(); returnErr == nil { + if err := fpKVs.Close(); returnErr == nil { returnErr = err } }() - fpValues, err := createFile(valuesFile) - if err != nil { - return err - } - defer func() { - if err := fpValues.Close(); returnErr == nil { - returnErr = err - } - }() nodesWriter := bufio.NewWriter(fpNodes) - keysWriter := bufio.NewWriter(fpKeys) - valuesWriter := bufio.NewWriter(fpValues) + kvsWriter := bufio.NewWriter(fpKVs) - w := newSnapshotWriter(nodesWriter, keysWriter, valuesWriter) - rootIndex, _, err = w.writeRecursive(t.root) + w := newSnapshotWriter(nodesWriter, kvsWriter) + rootIndex, err = w.writeRecursive(t.root) if err != nil { return err } @@ -255,32 +294,36 @@ func (t *Tree) WriteSnapshot(snapshotDir string) (returnErr error) { if err := nodesWriter.Flush(); err != nil { return err } - if err := keysWriter.Flush(); err != nil { - return err - } - if err := valuesWriter.Flush(); err != nil { + if err := kvsWriter.Flush(); err != nil { return err } - if err := fpKeys.Sync(); err != nil { + if err := fpKVs.Sync(); err != nil { return err } - if err := fpValues.Sync(); err != nil { + if err := fpNodes.Sync(); err != nil { return err } - if err := fpNodes.Sync(); err != nil { + + // re-open kvs file for reading + input, err := os.Open(kvsFile) + if err != nil { return err } + defer input.Close() + if err := buildIndex(input, kvsIndexFile, snapshotDir, int(t.root.Size())); err != nil { + return fmt.Errorf("build MPHF index failed: %w", err) + } } // write metadata var metadataBuf [SizeMetadata]byte binary.LittleEndian.PutUint32(metadataBuf[:], SnapshotFileMagic) binary.LittleEndian.PutUint32(metadataBuf[4:], SnapshotFormat) - binary.LittleEndian.PutUint64(metadataBuf[8:], uint64(t.version)) - binary.LittleEndian.PutUint32(metadataBuf[16:], rootIndex) + binary.LittleEndian.PutUint32(metadataBuf[8:], t.version) + binary.LittleEndian.PutUint32(metadataBuf[12:], rootIndex) - metadataFile := filepath.Join(snapshotDir, "metadata") + metadataFile := filepath.Join(snapshotDir, FileNameMetadata) fpMetadata, err := createFile(metadataFile) if err != nil { return err @@ -299,109 +342,154 @@ func (t *Tree) WriteSnapshot(snapshotDir string) (returnErr error) { } type snapshotWriter struct { - nodesWriter, keysWriter, valuesWriter io.Writer - nodeIndex uint32 - keysOffset, valuesOffset uint64 + nodesWriter, kvWriter io.Writer + + // record the current node index + nodeIndex uint32 + + // record the current writing offset in kvs file + kvsOffset uint64 } -func newSnapshotWriter(nodesWriter, keysWriter, valuesWriter io.Writer) *snapshotWriter { +func newSnapshotWriter(nodesWriter, kvsWriter io.Writer) *snapshotWriter { return &snapshotWriter{ - nodesWriter: nodesWriter, - keysWriter: keysWriter, - valuesWriter: valuesWriter, + nodesWriter: nodesWriter, + kvWriter: kvsWriter, } } -// writeKey append key to keys file -func (w *snapshotWriter) writeKey(key []byte) (uint64, error) { - var buf [SizeKeyLen]byte - if len(key) > math.MaxUint32 { - return 0, fmt.Errorf("key length overflow: %d", len(key)) - } - binary.LittleEndian.PutUint32(buf[:], uint32(len(key))) - if _, err := w.keysWriter.Write(buf[:]); err != nil { - return 0, err +// writeKeyValue append key-value pair to kvs file and record the offset +func (w *snapshotWriter) writeKeyValue(key, value []byte) error { + var numBuf [4]byte + + binary.LittleEndian.PutUint32(numBuf[:], uint32(len(key))) + if _, err := w.kvWriter.Write(numBuf[:]); err != nil { + return err } - if _, err := w.keysWriter.Write(key); err != nil { - return 0, err + if _, err := w.kvWriter.Write(key); err != nil { + return err } - offset := w.keysOffset - w.keysOffset += SizeKeyLen + uint64(len(key)) - return offset, nil -} -// writeValue append value to values file -func (w *snapshotWriter) writeValue(value []byte) (uint64, error) { - var buf [SizeValueLen]byte - if len(value) > math.MaxUint32 { - return 0, fmt.Errorf("value length overflow: %d", len(value)) - } - binary.LittleEndian.PutUint32(buf[:], uint32(len(value))) - if _, err := w.valuesWriter.Write(buf[:]); err != nil { - return 0, err + binary.LittleEndian.PutUint32(numBuf[:], uint32(len(value))) + if _, err := w.kvWriter.Write(numBuf[:]); err != nil { + return err } - if _, err := w.valuesWriter.Write(value); err != nil { - return 0, err + if _, err := w.kvWriter.Write(value); err != nil { + return err } - offset := w.valuesOffset - w.valuesOffset += SizeValueLen + uint64(len(value)) - return offset, nil + + w.kvsOffset += 4 + 4 + uint64(len(key)) + uint64(len(value)) + return nil } // writeRecursive write the node recursively in depth-first post-order, -// returns `(nodeIndex, offset of minimal key in subtree, err)`. -func (w *snapshotWriter) writeRecursive(node Node) (uint32, uint64, error) { - var ( - buf [SizeNodeWithoutHash]byte - minimalKeyOffset uint64 - ) +// returns `(nodeIndex, err)`. +func (w *snapshotWriter) writeRecursive(node Node) (uint32, error) { + var buf [SizeNodeWithoutHash]byte - buf[OffsetHeight] = byte(node.Height()) - if node.Version() > math.MaxUint32 { - return 0, 0, fmt.Errorf("version overflow: %d", node.Version()) - } - binary.LittleEndian.PutUint32(buf[OffsetVersion:], uint32(node.Version())) - binary.LittleEndian.PutUint64(buf[OffsetSize:], uint64(node.Size())) + buf[OffsetHeight] = node.Height() + binary.LittleEndian.PutUint32(buf[OffsetVersion:], node.Version()) if isLeaf(node) { - offset, err := w.writeKey(node.Key()) - if err != nil { - return 0, 0, err + keyOffset := w.kvsOffset + if err := w.writeKeyValue(node.Key(), node.Value()); err != nil { + return 0, err } - binary.LittleEndian.PutUint64(buf[OffsetKey:], offset) - minimalKeyOffset = offset - offset, err = w.writeValue(node.Value()) - if err != nil { - return 0, 0, err - } - binary.LittleEndian.PutUint64(buf[OffsetValue:], offset) + binary.LittleEndian.PutUint64(buf[OffsetKeyOffset:], keyOffset) } else { - // it use the minimal key from right subtree, but propagate the minimal key from left subtree. - nodeIndex, keyOffset, err := w.writeRecursive(node.Right()) - if err != nil { - return 0, 0, err - } - binary.LittleEndian.PutUint64(buf[OffsetKey:], keyOffset) - binary.LittleEndian.PutUint32(buf[OffsetRight:], nodeIndex) + binary.LittleEndian.PutUint32(buf[OffsetSize:], uint32(node.Size())) - nodeIndex, minimalKeyOffset, err = w.writeRecursive(node.Left()) + // store the minimal key from right subtree, but propagate the one from left subtree + leftIndex, err := w.writeRecursive(node.Left()) if err != nil { - return 0, 0, err + return 0, err + } + if _, err = w.writeRecursive(node.Right()); err != nil { + return 0, err } - binary.LittleEndian.PutUint32(buf[OffsetLeft:], nodeIndex) + binary.LittleEndian.PutUint32(buf[OffsetKeyNode:], leftIndex+1) } if _, err := w.nodesWriter.Write(buf[:]); err != nil { - return 0, 0, err + return 0, err } if _, err := w.nodesWriter.Write(node.Hash()); err != nil { - return 0, 0, err + return 0, err } i := w.nodeIndex w.nodeIndex++ - return i, minimalKeyOffset, nil + return i, nil +} + +// buildIndex build MPHF index for the kvs file. +func buildIndex(input *os.File, idxPath, tmpDir string, count int) error { + var numBuf [4]byte + + rs, err := recsplit.NewRecSplit(recsplit.RecSplitArgs{ + KeyCount: count, + Enums: false, + BucketSize: 2000, + LeafSize: 8, + TmpDir: tmpDir, + IndexFile: idxPath, + EtlBufLimit: etl.BufferOptimalSize / 2, + }) + if err != nil { + return err + } + + defer rs.Close() + + for { + if _, err := input.Seek(0, 0); err != nil { + return err + } + reader := bufio.NewReader(input) + + var pos uint64 + for { + if _, err := io.ReadFull(reader, numBuf[:]); err != nil { + if err == io.EOF { + break + } + return err + } + len1 := uint64(binary.LittleEndian.Uint32(numBuf[:])) + key := make([]byte, len1) + if _, err := io.ReadFull(reader, key); err != nil { + return err + } + + // skip the value part + if _, err := io.ReadFull(reader, numBuf[:]); err != nil { + return err + } + len2 := uint64(binary.LittleEndian.Uint32(numBuf[:])) + if _, err := io.CopyN(io.Discard, reader, int64(len2)); err != nil { + return err + } + + if err := rs.AddKey(key, pos); err != nil { + return err + } + pos += 8 + len1 + len2 + } + + if err := rs.Build(); err != nil { + if rs.Collision() { + rs.ResetNextSalt() + continue + } + + return err + } + + break + } + + return nil } func Mmap(f *os.File) ([]byte, *[mmap.MaxMapSize]byte, error) { diff --git a/memiavl/tree.go b/memiavl/tree.go index f566929379..aa9ec8a358 100644 --- a/memiavl/tree.go +++ b/memiavl/tree.go @@ -2,22 +2,27 @@ package memiavl import ( "crypto/sha256" + "errors" + "math" ) var emptyHash = sha256.New().Sum(nil) // verify change sets by replay them to rebuild iavl tree and verify the root hashes type Tree struct { - version int64 + version uint32 // root node of empty tree is represented as `nil` root Node - initialVersion int64 + initialVersion uint32 } // NewEmptyTree creates an empty tree at an arbitrary version. func NewEmptyTree(version int64) *Tree { - return &Tree{version: version} + if version >= math.MaxUint32 { + panic("version overflows uint32") + } + return &Tree{version: uint32(version)} } // New creates an empty tree at genesis version @@ -28,8 +33,11 @@ func New() *Tree { // New creates a empty tree with initial-version, // it happens when a new store created at the middle of the chain. func NewWithInitialVersion(initialVersion int64) *Tree { + if initialVersion >= math.MaxUint32 { + panic("version overflows uint32") + } tree := New() - tree.initialVersion = initialVersion + tree.initialVersion = uint32(initialVersion) return tree } @@ -39,7 +47,7 @@ func NewFromSnapshot(snapshot *Snapshot) *Tree { return NewEmptyTree(int64(snapshot.Version())) } return &Tree{ - version: int64(snapshot.Version()), + version: snapshot.Version(), root: snapshot.RootNode(), } } @@ -58,6 +66,10 @@ func (t *Tree) SaveVersion(updateHash bool) ([]byte, int64, error) { if updateHash { hash = t.root.Hash() } + + if t.version >= uint32(math.MaxUint32) { + return nil, 0, errors.New("version overflows uint32") + } t.version++ // to be compatible with existing golang iavl implementation. @@ -66,12 +78,12 @@ func (t *Tree) SaveVersion(updateHash bool) ([]byte, int64, error) { t.version = t.initialVersion } - return hash, t.version, nil + return hash, int64(t.version), nil } // Version returns the current tree version func (t *Tree) Version() int64 { - return t.version + return int64(t.version) } // RootHash updates the hashes and return the current root hash @@ -81,3 +93,11 @@ func (t *Tree) RootHash() []byte { } return t.root.Hash() } + +func (t *Tree) Get(key []byte) []byte { + if t.root == nil { + return nil + } + + return t.root.Get(key) +} diff --git a/versiondb/client/restore_app_db.go b/versiondb/client/restore_app_db.go index ad76cb3331..a01e77ac6e 100644 --- a/versiondb/client/restore_app_db.go +++ b/versiondb/client/restore_app_db.go @@ -283,7 +283,7 @@ func encodeNode(w io.Writer, node memiavl.PersistedNode) error { if _, err := w.Write(buf[:n]); err != nil { return err } - n = binary.PutVarint(buf[:], node.Version()) + n = binary.PutVarint(buf[:], int64(node.Version())) if _, err := w.Write(buf[:n]); err != nil { return err } diff --git a/versiondb/go.mod b/versiondb/go.mod index 145c4dcd4d..7b552ddf72 100644 --- a/versiondb/go.mod +++ b/versiondb/go.mod @@ -27,11 +27,13 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect + github.com/VictoriaMetrics/metrics v1.23.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -57,6 +59,7 @@ require ( github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/golang/glog v1.0.0 // indirect @@ -81,6 +84,7 @@ require ( github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect + github.com/ledgerwatch/log/v3 v3.7.0 // indirect github.com/lib/pq v1.10.6 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -107,6 +111,7 @@ require ( github.com/rs/cors v1.8.2 // indirect github.com/rs/zerolog v1.27.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -115,9 +120,13 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.5.0 // indirect + github.com/torquem-ch/mdbx-go v0.27.5 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/histogram v1.2.0 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect + go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect golang.org/x/net v0.7.0 // indirect diff --git a/versiondb/go.sum b/versiondb/go.sum index fbe9237d6d..cf52fc477c 100644 --- a/versiondb/go.sum +++ b/versiondb/go.sum @@ -79,6 +79,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/VictoriaMetrics/metrics v1.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0= +github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= @@ -157,6 +159,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -339,6 +343,8 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= @@ -597,6 +603,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/ledgerwatch/log/v3 v3.7.0 h1:aFPEZdwZx4jzA3+/Pf8wNDN5tCI0cIolq/kfvgcM+og= +github.com/ledgerwatch/log/v3 v3.7.0/go.mod h1:J2Jl6zV/58LeA6LTaVVnCGyf1/cYYSEOOLHY4ZN8S2A= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -726,6 +734,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -897,6 +906,8 @@ github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk1 github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/torquem-ch/mdbx-go v0.27.5 h1:bbhXQGFCmoxbRDXKYEJwxSOOTeBKwoD4pFBUpK9+V1g= +github.com/torquem-ch/mdbx-go v0.27.5/go.mod h1:T2fsoJDVppxfAPTLd1svUgH1kpPmeXdPESmroSHcL1E= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= @@ -911,8 +922,12 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -945,6 +960,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= @@ -1095,6 +1112,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1176,6 +1194,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=