diff --git a/coverage/coverage.out b/coverage/coverage.out index 3ece468..a5ca578 100644 --- a/coverage/coverage.out +++ b/coverage/coverage.out @@ -1,93 +1,4 @@ mode: set -github.com/echovault/echovault/internal/aof/engine.go:50.56,51.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:51.30,53.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:56.57,57.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:57.30,59.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:62.59,63.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:63.30,65.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:68.58,69.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:69.30,71.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:74.59,75.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:75.30,77.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:80.82,81.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:81.30,83.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:86.89,87.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:87.30,89.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:92.73,93.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:93.30,95.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:98.82,99.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:99.30,101.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:104.78,105.30 1 1 -github.com/echovault/echovault/internal/aof/engine.go:105.30,107.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:110.69,118.29 1 1 -github.com/echovault/echovault/internal/aof/engine.go:118.30,118.31 0 0 -github.com/echovault/echovault/internal/aof/engine.go:119.30,119.31 0 0 -github.com/echovault/echovault/internal/aof/engine.go:120.57,120.71 1 0 -github.com/echovault/echovault/internal/aof/engine.go:121.63,121.64 0 0 -github.com/echovault/echovault/internal/aof/engine.go:122.44,122.45 0 0 -github.com/echovault/echovault/internal/aof/engine.go:127.2,127.33 1 1 -github.com/echovault/echovault/internal/aof/engine.go:127.33,129.3 1 1 -github.com/echovault/echovault/internal/aof/engine.go:132.2,139.16 2 1 -github.com/echovault/echovault/internal/aof/engine.go:139.16,141.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:142.2,152.16 3 1 -github.com/echovault/echovault/internal/aof/engine.go:152.16,154.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:155.2,159.12 2 1 -github.com/echovault/echovault/internal/aof/engine.go:159.12,160.7 1 1 -github.com/echovault/echovault/internal/aof/engine.go:160.7,162.54 2 1 -github.com/echovault/echovault/internal/aof/engine.go:162.54,164.5 1 0 -github.com/echovault/echovault/internal/aof/engine.go:168.2,168.20 1 1 -github.com/echovault/echovault/internal/aof/engine.go:171.52,173.2 1 1 -github.com/echovault/echovault/internal/aof/engine.go:175.42,183.62 5 1 -github.com/echovault/echovault/internal/aof/engine.go:183.62,185.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:188.2,188.54 1 1 -github.com/echovault/echovault/internal/aof/engine.go:188.54,190.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:192.2,192.12 1 1 -github.com/echovault/echovault/internal/aof/engine.go:195.39,196.55 1 1 -github.com/echovault/echovault/internal/aof/engine.go:196.55,198.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:199.2,199.53 1 1 -github.com/echovault/echovault/internal/aof/engine.go:199.53,201.3 1 0 -github.com/echovault/echovault/internal/aof/engine.go:202.2,202.12 1 1 -github.com/echovault/echovault/internal/config/config.go:64.34,70.24 3 0 -github.com/echovault/echovault/internal/config/config.go:70.24,72.35 2 0 -github.com/echovault/echovault/internal/config/config.go:72.35,74.5 1 0 -github.com/echovault/echovault/internal/config/config.go:75.4,75.22 1 0 -github.com/echovault/echovault/internal/config/config.go:75.22,77.5 1 0 -github.com/echovault/echovault/internal/config/config.go:78.4,79.14 2 0 -github.com/echovault/echovault/internal/config/config.go:82.2,82.115 1 0 -github.com/echovault/echovault/internal/config/config.go:82.115,85.3 2 0 -github.com/echovault/echovault/internal/config/config.go:87.2,90.29 2 0 -github.com/echovault/echovault/internal/config/config.go:90.29,91.86 1 0 -github.com/echovault/echovault/internal/config/config.go:91.86,93.5 1 0 -github.com/echovault/echovault/internal/config/config.go:93.7,95.5 1 0 -github.com/echovault/echovault/internal/config/config.go:96.4,97.14 2 0 -github.com/echovault/echovault/internal/config/config.go:100.2,103.59 2 0 -github.com/echovault/echovault/internal/config/config.go:103.59,105.17 2 0 -github.com/echovault/echovault/internal/config/config.go:105.17,107.4 1 0 -github.com/echovault/echovault/internal/config/config.go:108.3,109.13 2 0 -github.com/echovault/echovault/internal/config/config.go:112.2,121.88 2 0 -github.com/echovault/echovault/internal/config/config.go:121.88,128.23 3 0 -github.com/echovault/echovault/internal/config/config.go:128.23,130.5 1 0 -github.com/echovault/echovault/internal/config/config.go:131.4,132.14 2 0 -github.com/echovault/echovault/internal/config/config.go:135.2,139.24 2 0 -github.com/echovault/echovault/internal/config/config.go:139.24,140.36 1 0 -github.com/echovault/echovault/internal/config/config.go:140.36,142.5 1 0 -github.com/echovault/echovault/internal/config/config.go:143.4,144.14 2 0 -github.com/echovault/echovault/internal/config/config.go:147.2,219.22 25 0 -github.com/echovault/echovault/internal/config/config.go:219.22,221.45 1 0 -github.com/echovault/echovault/internal/config/config.go:221.45,222.14 1 0 -github.com/echovault/echovault/internal/config/config.go:223.9,224.17 1 0 -github.com/echovault/echovault/internal/config/config.go:224.17,225.36 1 0 -github.com/echovault/echovault/internal/config/config.go:225.36,227.6 1 0 -github.com/echovault/echovault/internal/config/config.go:230.4,232.22 2 0 -github.com/echovault/echovault/internal/config/config.go:232.22,233.59 1 0 -github.com/echovault/echovault/internal/config/config.go:233.59,235.6 1 0 -github.com/echovault/echovault/internal/config/config.go:238.4,238.39 1 0 -github.com/echovault/echovault/internal/config/config.go:238.39,239.59 1 0 -github.com/echovault/echovault/internal/config/config.go:239.59,241.6 1 0 -github.com/echovault/echovault/internal/config/config.go:247.2,249.45 2 0 -github.com/echovault/echovault/internal/config/config.go:249.45,251.3 1 0 -github.com/echovault/echovault/internal/config/config.go:253.2,253.18 1 0 -github.com/echovault/echovault/internal/config/default.go:8.29,38.2 1 0 github.com/echovault/echovault/internal/aof/log/store.go:46.60,47.34 1 1 github.com/echovault/echovault/internal/aof/log/store.go:47.34,49.3 1 1 github.com/echovault/echovault/internal/aof/log/store.go:52.61,53.34 1 1 @@ -112,7 +23,7 @@ github.com/echovault/echovault/internal/aof/log/store.go:106.2,106.51 1 1 github.com/echovault/echovault/internal/aof/log/store.go:106.51,107.13 1 1 github.com/echovault/echovault/internal/aof/log/store.go:107.13,108.8 1 1 github.com/echovault/echovault/internal/aof/log/store.go:108.8,110.40 2 1 -github.com/echovault/echovault/internal/aof/log/store.go:110.40,113.11 3 1 +github.com/echovault/echovault/internal/aof/log/store.go:110.40,113.11 3 0 github.com/echovault/echovault/internal/aof/log/store.go:115.5,116.41 2 0 github.com/echovault/echovault/internal/aof/log/store.go:121.2,121.19 1 1 github.com/echovault/echovault/internal/aof/log/store.go:124.55,126.21 1 1 @@ -198,6 +109,95 @@ github.com/echovault/echovault/internal/aof/preamble/store.go:185.43,187.4 1 1 github.com/echovault/echovault/internal/aof/preamble/store.go:189.2,189.35 1 1 github.com/echovault/echovault/internal/aof/preamble/store.go:189.35,191.3 1 1 github.com/echovault/echovault/internal/aof/preamble/store.go:192.2,192.14 1 1 +github.com/echovault/echovault/internal/config/config.go:64.34,70.24 3 0 +github.com/echovault/echovault/internal/config/config.go:70.24,72.35 2 0 +github.com/echovault/echovault/internal/config/config.go:72.35,74.5 1 0 +github.com/echovault/echovault/internal/config/config.go:75.4,75.22 1 0 +github.com/echovault/echovault/internal/config/config.go:75.22,77.5 1 0 +github.com/echovault/echovault/internal/config/config.go:78.4,79.14 2 0 +github.com/echovault/echovault/internal/config/config.go:82.2,82.115 1 0 +github.com/echovault/echovault/internal/config/config.go:82.115,85.3 2 0 +github.com/echovault/echovault/internal/config/config.go:87.2,90.29 2 0 +github.com/echovault/echovault/internal/config/config.go:90.29,91.86 1 0 +github.com/echovault/echovault/internal/config/config.go:91.86,93.5 1 0 +github.com/echovault/echovault/internal/config/config.go:93.7,95.5 1 0 +github.com/echovault/echovault/internal/config/config.go:96.4,97.14 2 0 +github.com/echovault/echovault/internal/config/config.go:100.2,103.59 2 0 +github.com/echovault/echovault/internal/config/config.go:103.59,105.17 2 0 +github.com/echovault/echovault/internal/config/config.go:105.17,107.4 1 0 +github.com/echovault/echovault/internal/config/config.go:108.3,109.13 2 0 +github.com/echovault/echovault/internal/config/config.go:112.2,121.88 2 0 +github.com/echovault/echovault/internal/config/config.go:121.88,128.23 3 0 +github.com/echovault/echovault/internal/config/config.go:128.23,130.5 1 0 +github.com/echovault/echovault/internal/config/config.go:131.4,132.14 2 0 +github.com/echovault/echovault/internal/config/config.go:135.2,139.24 2 0 +github.com/echovault/echovault/internal/config/config.go:139.24,140.36 1 0 +github.com/echovault/echovault/internal/config/config.go:140.36,142.5 1 0 +github.com/echovault/echovault/internal/config/config.go:143.4,144.14 2 0 +github.com/echovault/echovault/internal/config/config.go:147.2,219.22 25 0 +github.com/echovault/echovault/internal/config/config.go:219.22,221.45 1 0 +github.com/echovault/echovault/internal/config/config.go:221.45,222.14 1 0 +github.com/echovault/echovault/internal/config/config.go:223.9,224.17 1 0 +github.com/echovault/echovault/internal/config/config.go:224.17,225.36 1 0 +github.com/echovault/echovault/internal/config/config.go:225.36,227.6 1 0 +github.com/echovault/echovault/internal/config/config.go:230.4,232.22 2 0 +github.com/echovault/echovault/internal/config/config.go:232.22,233.59 1 0 +github.com/echovault/echovault/internal/config/config.go:233.59,235.6 1 0 +github.com/echovault/echovault/internal/config/config.go:238.4,238.39 1 0 +github.com/echovault/echovault/internal/config/config.go:238.39,239.59 1 0 +github.com/echovault/echovault/internal/config/config.go:239.59,241.6 1 0 +github.com/echovault/echovault/internal/config/config.go:247.2,249.45 2 0 +github.com/echovault/echovault/internal/config/config.go:249.45,251.3 1 0 +github.com/echovault/echovault/internal/config/config.go:253.2,253.18 1 0 +github.com/echovault/echovault/internal/config/default.go:8.29,38.2 1 0 +github.com/echovault/echovault/internal/aof/engine.go:50.56,51.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:51.30,53.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:56.57,57.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:57.30,59.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:62.59,63.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:63.30,65.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:68.58,69.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:69.30,71.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:74.59,75.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:75.30,77.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:80.82,81.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:81.30,83.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:86.89,87.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:87.30,89.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:92.73,93.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:93.30,95.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:98.82,99.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:99.30,101.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:104.78,105.30 1 1 +github.com/echovault/echovault/internal/aof/engine.go:105.30,107.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:110.69,118.29 1 1 +github.com/echovault/echovault/internal/aof/engine.go:118.30,118.31 0 0 +github.com/echovault/echovault/internal/aof/engine.go:119.30,119.31 0 0 +github.com/echovault/echovault/internal/aof/engine.go:120.57,120.71 1 0 +github.com/echovault/echovault/internal/aof/engine.go:121.63,121.64 0 0 +github.com/echovault/echovault/internal/aof/engine.go:122.44,122.45 0 0 +github.com/echovault/echovault/internal/aof/engine.go:127.2,127.33 1 1 +github.com/echovault/echovault/internal/aof/engine.go:127.33,129.3 1 1 +github.com/echovault/echovault/internal/aof/engine.go:132.2,139.16 2 1 +github.com/echovault/echovault/internal/aof/engine.go:139.16,141.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:142.2,152.16 3 1 +github.com/echovault/echovault/internal/aof/engine.go:152.16,154.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:155.2,159.12 2 1 +github.com/echovault/echovault/internal/aof/engine.go:159.12,160.7 1 1 +github.com/echovault/echovault/internal/aof/engine.go:160.7,162.54 2 1 +github.com/echovault/echovault/internal/aof/engine.go:162.54,164.5 1 0 +github.com/echovault/echovault/internal/aof/engine.go:168.2,168.20 1 1 +github.com/echovault/echovault/internal/aof/engine.go:171.52,173.2 1 1 +github.com/echovault/echovault/internal/aof/engine.go:175.42,183.62 5 1 +github.com/echovault/echovault/internal/aof/engine.go:183.62,185.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:188.2,188.54 1 1 +github.com/echovault/echovault/internal/aof/engine.go:188.54,190.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:192.2,192.12 1 1 +github.com/echovault/echovault/internal/aof/engine.go:195.39,196.55 1 1 +github.com/echovault/echovault/internal/aof/engine.go:196.55,198.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:199.2,199.53 1 1 +github.com/echovault/echovault/internal/aof/engine.go:199.53,201.3 1 0 +github.com/echovault/echovault/internal/aof/engine.go:202.2,202.12 1 1 github.com/echovault/echovault/internal/eviction/lfu.go:35.29,42.2 3 1 github.com/echovault/echovault/internal/eviction/lfu.go:44.34,46.2 1 1 github.com/echovault/echovault/internal/eviction/lfu.go:48.44,50.54 1 1 @@ -669,105 +669,12 @@ github.com/echovault/echovault/internal/modules/acl/user.go:290.40,305.2 1 1 github.com/echovault/echovault/internal/modules/acl/user.go:307.46,308.24 1 1 github.com/echovault/echovault/internal/modules/acl/user.go:308.24,310.3 1 0 github.com/echovault/echovault/internal/modules/acl/user.go:311.2,311.26 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:27.78,33.29 4 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:33.29,34.54 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:34.54,40.42 4 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:40.42,42.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:44.4,47.12 3 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:50.3,50.36 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:50.36,57.43 5 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:57.43,59.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:61.4,63.21 2 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:67.2,69.25 2 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:72.76,76.35 3 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:76.35,77.65 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:77.65,78.41 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:78.41,80.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:81.4,81.12 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:83.3,83.13 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:86.2,86.51 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:89.75,90.29 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:91.9,96.36 4 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:96.36,97.66 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:97.66,98.52 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:98.52,102.6 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:103.5,103.13 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:105.4,106.14 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:108.3,109.26 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:111.9,115.56 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:115.56,117.4 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:118.3,118.53 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:118.53,122.37 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:122.37,123.67 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:123.67,124.53 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:124.53,125.59 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:125.59,129.8 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:131.6,131.14 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:133.5,133.54 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:133.54,136.6 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:138.9,138.61 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:138.61,142.37 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:142.37,143.67 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:143.67,144.53 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:144.53,146.24 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:146.24,149.8 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:151.6,151.14 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:153.5,153.33 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:153.33,156.6 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:158.9,158.60 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:158.60,162.37 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:162.37,163.67 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:163.67,164.53 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:164.53,165.55 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:165.55,169.8 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:171.6,171.14 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:173.5,173.50 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:173.50,176.6 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:178.9,180.4 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:181.3,182.26 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:183.10,184.54 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:188.75,190.2 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:192.36,200.84 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:200.84,204.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:213.84,217.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:225.86,229.7 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:238.86,242.7 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:252.86,256.7 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:267.84,271.5 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:272.73,273.49 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:273.49,275.6 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:276.5,276.45 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:285.84,289.5 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:290.73,292.18 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:292.18,294.6 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:295.5,295.53 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:304.84,308.5 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:309.73,310.47 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:310.47,312.6 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:313.5,313.45 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:321.84,325.5 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:335.86,339.7 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:340.75,341.34 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:341.34,343.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:344.7,345.34 2 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:345.34,347.8 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:348.7,348.75 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:348.75,350.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:351.7,351.47 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:361.86,365.7 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:366.75,367.35 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:367.35,369.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:370.7,371.47 2 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:380.86,384.7 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:385.75,388.38 3 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:388.38,390.8 1 1 -github.com/echovault/echovault/internal/modules/admin/commands.go:391.7,391.30 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:24.68,25.29 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:26.10,27.54 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:28.9,29.34 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:30.9,31.94 1 1 +github.com/echovault/echovault/internal/modules/connection/commands.go:24.68,25.29 1 0 +github.com/echovault/echovault/internal/modules/connection/commands.go:26.10,27.54 1 0 +github.com/echovault/echovault/internal/modules/connection/commands.go:28.9,29.34 1 0 +github.com/echovault/echovault/internal/modules/connection/commands.go:30.9,31.94 1 0 github.com/echovault/echovault/internal/modules/connection/commands.go:35.36,45.84 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:45.84,51.5 1 1 +github.com/echovault/echovault/internal/modules/connection/commands.go:45.84,51.5 1 0 github.com/echovault/echovault/internal/modules/generic/commands.go:33.67,35.16 2 1 github.com/echovault/echovault/internal/modules/generic/commands.go:35.16,37.3 1 0 github.com/echovault/echovault/internal/modules/generic/commands.go:39.2,46.16 7 1 @@ -1089,10 +996,10 @@ github.com/echovault/echovault/internal/modules/hash/commands.go:301.2,301.38 1 github.com/echovault/echovault/internal/modules/hash/commands.go:301.38,303.17 2 1 github.com/echovault/echovault/internal/modules/hash/commands.go:303.17,304.41 1 1 github.com/echovault/echovault/internal/modules/hash/commands.go:304.41,306.13 2 1 -github.com/echovault/echovault/internal/modules/hash/commands.go:308.4,308.42 1 0 -github.com/echovault/echovault/internal/modules/hash/commands.go:308.42,311.13 3 0 -github.com/echovault/echovault/internal/modules/hash/commands.go:313.4,313.38 1 0 -github.com/echovault/echovault/internal/modules/hash/commands.go:313.38,315.13 2 0 +github.com/echovault/echovault/internal/modules/hash/commands.go:308.4,308.42 1 1 +github.com/echovault/echovault/internal/modules/hash/commands.go:308.42,311.13 3 1 +github.com/echovault/echovault/internal/modules/hash/commands.go:313.4,313.38 1 1 +github.com/echovault/echovault/internal/modules/hash/commands.go:313.38,315.13 2 1 github.com/echovault/echovault/internal/modules/hash/commands.go:320.2,320.25 1 1 github.com/echovault/echovault/internal/modules/hash/commands.go:323.68,325.16 2 1 github.com/echovault/echovault/internal/modules/hash/commands.go:325.16,327.3 1 0 @@ -1218,13 +1125,13 @@ github.com/echovault/echovault/internal/modules/hash/key_funcs.go:144.2,148.8 1 github.com/echovault/echovault/internal/modules/hash/key_funcs.go:151.74,152.18 1 1 github.com/echovault/echovault/internal/modules/hash/key_funcs.go:152.18,154.3 1 1 github.com/echovault/echovault/internal/modules/hash/key_funcs.go:155.2,159.8 1 1 -github.com/echovault/echovault/internal/modules/list/commands.go:27.68,29.16 2 0 +github.com/echovault/echovault/internal/modules/list/commands.go:27.68,29.16 2 1 github.com/echovault/echovault/internal/modules/list/commands.go:29.16,31.3 1 0 -github.com/echovault/echovault/internal/modules/list/commands.go:33.2,36.16 3 0 -github.com/echovault/echovault/internal/modules/list/commands.go:36.16,39.3 1 0 -github.com/echovault/echovault/internal/modules/list/commands.go:41.2,41.90 1 0 -github.com/echovault/echovault/internal/modules/list/commands.go:41.90,43.3 1 0 -github.com/echovault/echovault/internal/modules/list/commands.go:45.2,45.57 1 0 +github.com/echovault/echovault/internal/modules/list/commands.go:33.2,36.16 3 1 +github.com/echovault/echovault/internal/modules/list/commands.go:36.16,39.3 1 1 +github.com/echovault/echovault/internal/modules/list/commands.go:41.2,41.90 1 1 +github.com/echovault/echovault/internal/modules/list/commands.go:41.90,43.3 1 1 +github.com/echovault/echovault/internal/modules/list/commands.go:45.2,45.57 1 1 github.com/echovault/echovault/internal/modules/list/commands.go:48.70,50.16 2 1 github.com/echovault/echovault/internal/modules/list/commands.go:50.16,52.3 1 0 github.com/echovault/echovault/internal/modules/list/commands.go:54.2,58.9 4 1 @@ -1386,9 +1293,9 @@ github.com/echovault/echovault/internal/modules/list/key_funcs.go:27.2,31.8 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:34.73,35.19 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:35.19,37.3 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:38.2,42.8 1 1 -github.com/echovault/echovault/internal/modules/list/key_funcs.go:45.74,46.19 1 0 -github.com/echovault/echovault/internal/modules/list/key_funcs.go:46.19,48.3 1 0 -github.com/echovault/echovault/internal/modules/list/key_funcs.go:49.2,53.8 1 0 +github.com/echovault/echovault/internal/modules/list/key_funcs.go:45.74,46.19 1 1 +github.com/echovault/echovault/internal/modules/list/key_funcs.go:46.19,48.3 1 1 +github.com/echovault/echovault/internal/modules/list/key_funcs.go:49.2,53.8 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:56.76,57.19 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:57.19,59.3 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:60.2,64.8 1 1 @@ -1410,157 +1317,264 @@ github.com/echovault/echovault/internal/modules/list/key_funcs.go:115.2,119.8 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:122.75,123.19 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:123.19,125.3 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:126.2,130.8 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:34.51,35.32 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:35.32,37.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:41.57,42.32 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:42.32,45.3 2 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:48.61,59.33 3 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:59.33,61.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:63.2,63.16 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:66.28,67.12 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:67.12,68.7 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:68.7,73.40 3 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:73.40,74.30 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:74.30,79.21 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:79.21,81.7 1 0 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:85.4,85.33 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:90.34,92.2 1 0 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:94.40,96.2 1 0 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:98.51,101.40 3 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:101.40,103.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:104.2,105.11 2 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:108.53,111.40 3 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:111.40,113.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:114.2,115.13 2 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:118.44,120.2 1 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:122.36,129.2 4 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:131.34,138.2 4 1 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:140.59,145.35 4 0 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:145.35,147.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/channel.go:149.2,149.20 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:25.73,27.9 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:27.9,29.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:31.2,33.24 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:33.24,35.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:37.2,40.17 3 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:43.75,45.9 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:45.9,47.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:49.2,53.90 3 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:56.71,58.9 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:58.9,60.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:61.2,61.30 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:61.30,63.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:64.2,65.42 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:68.78,69.29 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:69.29,71.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:73.2,74.9 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:74.9,76.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:78.2,79.30 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:79.30,81.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:83.2,83.38 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:86.76,88.9 2 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:88.9,90.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:91.2,92.49 2 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:95.77,97.9 2 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:97.9,99.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:100.2,100.47 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:103.36,111.84 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:111.84,113.21 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:113.21,115.6 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:116.5,120.11 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:130.84,132.21 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:132.21,134.6 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:135.5,139.11 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:149.84,151.22 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:151.22,153.6 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:154.5,158.11 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:170.84,177.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:188.84,194.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:203.84,209.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:210.68,212.5 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:222.86,228.7 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:237.86,243.7 1 0 -github.com/echovault/echovault/internal/modules/pubsub/commands.go:253.86,259.7 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:33.26,38.2 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:40.101,47.17 5 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:47.17,49.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:51.2,51.37 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:51.37,55.75 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:55.75,57.4 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:59.3,59.23 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:59.23,62.19 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:62.19,64.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:64.10,66.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:67.4,68.31 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:68.31,73.20 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:73.20,75.6 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:76.5,76.47 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:78.9,80.47 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:80.47,85.20 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:85.20,87.6 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:93.110,98.17 4 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:98.17,100.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:102.2,105.24 3 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:105.24,106.19 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:106.19,109.40 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:109.40,110.31 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:110.31,111.14 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:113.5,113.34 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:113.34,116.6 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:118.9,121.40 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:121.40,122.31 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:122.31,123.14 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:125.5,125.34 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:125.34,128.6 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:136.2,136.38 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:136.38,137.30 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:137.30,138.54 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:138.54,141.5 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:147.2,147.17 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:147.17,148.36 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:148.36,150.40 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:150.40,152.58 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:152.58,153.35 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:153.35,156.7 2 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:157.6,157.14 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:160.5,160.30 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:160.30,161.35 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:161.35,164.7 2 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:170.2,171.39 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:171.39,173.3 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:175.2,175.20 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:178.82,182.38 3 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:182.38,184.29 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:184.29,185.35 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:185.35,187.5 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:188.4,188.12 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:191.3,191.41 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:191.41,193.4 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:197.51,204.19 5 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:204.19,205.39 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:205.39,206.26 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:206.26,209.5 2 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:211.3,212.21 2 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:215.2,217.38 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:217.38,219.78 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:219.78,222.12 3 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:225.3,225.50 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:225.50,228.4 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:231.2,231.53 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:234.32,239.38 4 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:239.38,240.51 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:240.51,242.4 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:244.2,244.14 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:247.52,252.35 4 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:252.35,254.66 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:254.66,256.4 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:257.3,257.20 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:257.20,259.12 2 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:261.3,261.106 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:263.2,263.20 1 1 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:266.47,271.38 4 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:271.38,273.3 1 0 -github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:275.2,275.17 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:55.56,56.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:56.30,58.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:61.59,62.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:62.30,64.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:67.64,68.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:68.30,70.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:73.59,74.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:74.30,76.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:79.59,80.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:80.30,82.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:85.60,86.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:86.30,88.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:91.82,92.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:92.30,94.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:97.77,98.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:98.30,100.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:103.73,104.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:104.30,106.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:109.89,110.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:110.30,112.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:115.65,122.30 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:122.31,122.32 0 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:123.31,123.32 0 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:124.52,126.4 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:127.71,127.72 0 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:128.48,128.49 0 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:129.43,131.4 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:134.2,134.33 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:134.33,136.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:138.2,138.34 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:138.34,139.13 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:139.13,140.8 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:140.8,142.62 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:142.62,143.50 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:143.50,145.7 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:151.2,151.15 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:154.44,174.58 7 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:174.58,177.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:180.2,182.16 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:182.16,183.37 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:183.37,186.18 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:186.18,189.5 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:190.4,190.24 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:191.9,194.4 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:197.2,198.16 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:198.16,201.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:202.2,202.35 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:202.35,205.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:207.2,209.20 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:209.20,210.53 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:210.53,213.4 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:217.2,222.16 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:222.16,225.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:227.2,228.49 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:228.49,230.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:233.2,236.16 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:236.16,239.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:242.2,243.16 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:243.16,246.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:249.2,254.16 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:254.16,257.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:258.2,258.39 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:258.39,261.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:262.2,262.33 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:262.33,264.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:265.2,265.34 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:265.34,268.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:271.2,272.58 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:272.58,274.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:277.2,278.16 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:278.16,281.3 2 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:282.2,282.15 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:282.15,283.35 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:283.35,285.4 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:289.2,289.39 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:289.39,291.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:292.2,292.32 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:292.32,294.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:297.2,302.12 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:305.39,307.50 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:307.50,309.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:310.2,310.16 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:310.16,312.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:314.2,317.16 3 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:317.16,319.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:321.2,321.52 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:321.52,323.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:325.2,325.46 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:325.46,327.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:329.2,334.50 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:334.50,336.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:337.2,337.16 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:337.16,339.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:341.2,342.16 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:342.16,344.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:346.2,348.58 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:348.58,350.3 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:352.2,354.94 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:354.94,356.3 1 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:358.2,360.12 2 1 +github.com/echovault/echovault/internal/snapshot/snapshot.go:363.46,365.2 1 0 +github.com/echovault/echovault/internal/snapshot/snapshot.go:367.42,369.2 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:27.78,33.29 4 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:33.29,34.54 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:34.54,40.42 4 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:40.42,42.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:44.4,47.12 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:50.3,50.36 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:50.36,57.43 5 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:57.43,59.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:61.4,63.21 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:67.2,69.25 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:72.76,76.35 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:76.35,77.65 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:77.65,78.41 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:78.41,80.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:81.4,81.12 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:83.3,83.13 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:86.2,86.51 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:89.75,90.29 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:91.9,96.36 4 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:96.36,97.66 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:97.66,98.52 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:98.52,102.6 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:103.5,103.13 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:105.4,106.14 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:108.3,109.26 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:111.9,115.56 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:115.56,117.4 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:118.3,118.53 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:118.53,122.37 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:122.37,123.67 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:123.67,124.53 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:124.53,125.59 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:125.59,129.8 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:131.6,131.14 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:133.5,133.54 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:133.54,136.6 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:138.9,138.61 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:138.61,142.37 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:142.37,143.67 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:143.67,144.53 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:144.53,146.24 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:146.24,149.8 2 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:151.6,151.14 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:153.5,153.33 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:153.33,156.6 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:158.9,158.60 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:158.60,162.37 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:162.37,163.67 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:163.67,164.53 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:164.53,165.55 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:165.55,169.8 3 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:171.6,171.14 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:173.5,173.50 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:173.50,176.6 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:178.9,180.4 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:181.3,182.26 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:183.10,184.54 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:188.75,190.2 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:192.36,200.84 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:200.84,204.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:213.84,217.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:225.86,229.7 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:238.86,242.7 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:252.86,256.7 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:267.84,271.5 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:272.73,273.49 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:273.49,275.6 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:276.5,276.45 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:285.84,289.5 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:290.73,292.18 2 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:292.18,294.6 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:295.5,295.53 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:304.84,308.5 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:309.73,310.47 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:310.47,312.6 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:313.5,313.45 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:321.84,325.5 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:335.86,339.7 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:340.75,341.34 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:341.34,343.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:344.7,345.34 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:345.34,347.8 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:348.7,348.75 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:348.75,350.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:351.7,351.47 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:361.86,365.7 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:366.75,367.35 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:367.35,369.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:370.7,371.47 2 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:380.86,384.7 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:385.75,388.38 3 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:388.38,390.8 1 1 +github.com/echovault/echovault/internal/modules/admin/commands.go:391.7,391.30 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:24.72,26.16 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:26.16,28.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:30.2,34.9 4 1 +github.com/echovault/echovault/internal/modules/string/commands.go:34.9,36.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:38.2,40.16 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:40.16,42.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:44.2,45.9 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:45.9,47.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:50.2,50.24 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:50.24,52.94 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:52.94,54.4 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:55.3,55.58 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:59.2,59.16 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:59.16,61.94 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:61.94,63.4 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:64.3,64.58 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:67.2,69.35 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:69.35,71.24 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:71.24,74.12 3 1 +github.com/echovault/echovault/internal/modules/string/commands.go:77.3,78.8 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:81.2,81.103 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:81.103,83.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:85.2,85.59 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:88.70,90.16 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:90.16,92.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:94.2,97.16 3 1 +github.com/echovault/echovault/internal/modules/string/commands.go:97.16,99.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:101.2,103.9 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:103.9,105.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:107.2,107.56 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:110.70,112.16 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:112.16,114.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:116.2,123.24 6 1 +github.com/echovault/echovault/internal/modules/string/commands.go:123.24,125.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:127.2,127.16 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:127.16,129.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:131.2,132.9 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:132.9,134.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:136.2,136.15 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:136.15,138.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:139.2,139.13 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:139.13,141.3 1 0 +github.com/echovault/echovault/internal/modules/string/commands.go:143.2,143.30 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:143.30,145.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:147.2,147.22 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:147.22,149.3 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:151.2,151.17 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:151.17,154.3 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:156.2,158.14 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:158.14,160.38 2 1 +github.com/echovault/echovault/internal/modules/string/commands.go:160.38,162.4 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:163.3,163.12 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:166.2,166.65 1 1 +github.com/echovault/echovault/internal/modules/string/commands.go:169.36,209.2 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:23.78,24.19 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:24.19,26.3 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:27.2,31.8 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:34.76,35.19 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:35.19,37.3 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:38.2,42.8 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:45.76,46.19 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:46.19,48.3 1 1 +github.com/echovault/echovault/internal/modules/string/key_funcs.go:49.2,53.8 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:26.68,28.16 2 1 github.com/echovault/echovault/internal/modules/set/commands.go:28.16,30.3 1 0 github.com/echovault/echovault/internal/modules/set/commands.go:32.2,37.16 4 1 @@ -1632,7 +1646,7 @@ github.com/echovault/echovault/internal/modules/set/commands.go:221.71,223.4 1 0 github.com/echovault/echovault/internal/modules/set/commands.go:223.9,225.4 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:228.2,230.37 2 1 github.com/echovault/echovault/internal/modules/set/commands.go:230.37,231.14 1 1 -github.com/echovault/echovault/internal/modules/set/commands.go:231.14,233.4 1 1 +github.com/echovault/echovault/internal/modules/set/commands.go:231.14,233.4 1 0 github.com/echovault/echovault/internal/modules/set/commands.go:234.3,235.10 2 1 github.com/echovault/echovault/internal/modules/set/commands.go:235.10,238.4 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:239.3,239.27 1 1 @@ -2676,168 +2690,1173 @@ github.com/echovault/echovault/internal/modules/sorted_set/utils.go:162.3,162.13 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:163.12,164.16 1 1 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:164.16,166.4 1 1 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:167.3,167.13 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:24.72,26.16 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:26.16,28.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:30.2,34.9 4 1 -github.com/echovault/echovault/internal/modules/string/commands.go:34.9,36.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:38.2,40.16 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:40.16,42.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:44.2,45.9 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:45.9,47.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:50.2,50.24 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:50.24,52.94 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:52.94,54.4 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:55.3,55.58 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:59.2,59.16 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:59.16,61.94 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:61.94,63.4 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:64.3,64.58 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:67.2,69.35 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:69.35,71.24 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:71.24,74.12 3 1 -github.com/echovault/echovault/internal/modules/string/commands.go:77.3,78.8 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:81.2,81.103 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:81.103,83.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:85.2,85.59 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:88.70,90.16 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:90.16,92.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:94.2,97.16 3 1 -github.com/echovault/echovault/internal/modules/string/commands.go:97.16,99.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:101.2,103.9 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:103.9,105.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:107.2,107.56 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:110.70,112.16 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:112.16,114.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:116.2,123.24 6 1 -github.com/echovault/echovault/internal/modules/string/commands.go:123.24,125.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:127.2,127.16 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:127.16,129.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:131.2,132.9 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:132.9,134.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:136.2,136.15 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:136.15,138.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:139.2,139.13 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:139.13,141.3 1 0 -github.com/echovault/echovault/internal/modules/string/commands.go:143.2,143.30 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:143.30,145.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:147.2,147.22 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:147.22,149.3 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:151.2,151.17 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:151.17,154.3 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:156.2,158.14 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:158.14,160.38 2 1 -github.com/echovault/echovault/internal/modules/string/commands.go:160.38,162.4 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:163.3,163.12 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:166.2,166.65 1 1 -github.com/echovault/echovault/internal/modules/string/commands.go:169.36,209.2 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:23.78,24.19 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:24.19,26.3 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:27.2,31.8 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:34.76,35.19 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:35.19,37.3 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:38.2,42.8 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:45.76,46.19 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:46.19,48.3 1 1 -github.com/echovault/echovault/internal/modules/string/key_funcs.go:49.2,53.8 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:55.56,56.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:56.30,58.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:61.59,62.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:62.30,64.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:67.64,68.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:68.30,70.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:73.59,74.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:74.30,76.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:79.59,80.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:80.30,82.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:85.60,86.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:86.30,88.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:91.82,92.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:92.30,94.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:97.77,98.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:98.30,100.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:103.73,104.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:104.30,106.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:109.89,110.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:110.30,112.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:115.65,122.30 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:122.31,122.32 0 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:123.31,123.32 0 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:124.52,126.4 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:127.71,127.72 0 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:128.48,128.49 0 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:129.43,131.4 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:134.2,134.33 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:134.33,136.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:138.2,138.34 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:138.34,139.13 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:139.13,140.8 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:140.8,142.62 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:142.62,143.50 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:143.50,145.7 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:151.2,151.15 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:154.44,174.58 7 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:174.58,177.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:180.2,182.16 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:182.16,183.37 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:183.37,186.18 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:186.18,189.5 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:190.4,190.24 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:191.9,194.4 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:197.2,198.16 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:198.16,201.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:202.2,202.35 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:202.35,205.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:207.2,209.20 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:209.20,210.53 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:210.53,213.4 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:217.2,222.16 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:222.16,225.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:227.2,228.49 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:228.49,230.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:233.2,236.16 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:236.16,239.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:242.2,243.16 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:243.16,246.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:249.2,254.16 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:254.16,257.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:258.2,258.39 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:258.39,261.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:262.2,262.33 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:262.33,264.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:265.2,265.34 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:265.34,268.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:271.2,272.58 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:272.58,274.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:277.2,278.16 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:278.16,281.3 2 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:282.2,282.15 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:282.15,283.35 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:283.35,285.4 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:289.2,289.39 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:289.39,291.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:292.2,292.32 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:292.32,294.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:297.2,302.12 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:305.39,307.50 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:307.50,309.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:310.2,310.16 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:310.16,312.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:314.2,317.16 3 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:317.16,319.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:321.2,321.52 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:321.52,323.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:325.2,325.46 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:325.46,327.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:329.2,334.50 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:334.50,336.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:337.2,337.16 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:337.16,339.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:341.2,342.16 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:342.16,344.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:346.2,348.58 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:348.58,350.3 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:352.2,354.94 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:354.94,356.3 1 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:358.2,360.12 2 1 -github.com/echovault/echovault/internal/snapshot/snapshot.go:363.46,365.2 1 0 -github.com/echovault/echovault/internal/snapshot/snapshot.go:367.42,369.2 1 1 +github.com/echovault/echovault/echovault/api_acl.go:126.71,128.23 2 1 +github.com/echovault/echovault/echovault/api_acl.go:128.23,130.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:131.2,132.16 2 1 +github.com/echovault/echovault/echovault/api_acl.go:132.16,134.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:135.2,135.45 1 1 +github.com/echovault/echovault/echovault/api_acl.go:139.55,141.16 2 1 +github.com/echovault/echovault/echovault/api_acl.go:141.16,143.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:144.2,144.45 1 1 +github.com/echovault/echovault/echovault/api_acl.go:155.62,158.18 2 1 +github.com/echovault/echovault/echovault/api_acl.go:158.18,160.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:160.8,162.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:164.2,164.21 1 1 +github.com/echovault/echovault/echovault/api_acl.go:164.21,166.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:168.2,168.17 1 1 +github.com/echovault/echovault/echovault/api_acl.go:168.17,170.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:172.2,172.21 1 1 +github.com/echovault/echovault/echovault/api_acl.go:172.21,174.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:176.2,176.20 1 1 +github.com/echovault/echovault/echovault/api_acl.go:176.20,178.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:180.2,180.20 1 1 +github.com/echovault/echovault/echovault/api_acl.go:180.20,182.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:184.2,184.24 1 1 +github.com/echovault/echovault/echovault/api_acl.go:184.24,186.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:188.2,188.50 1 1 +github.com/echovault/echovault/echovault/api_acl.go:188.50,190.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:192.2,192.53 1 1 +github.com/echovault/echovault/echovault/api_acl.go:192.53,194.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:196.2,196.49 1 1 +github.com/echovault/echovault/echovault/api_acl.go:196.49,198.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:200.2,200.52 1 1 +github.com/echovault/echovault/echovault/api_acl.go:200.52,202.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:204.2,204.50 1 1 +github.com/echovault/echovault/echovault/api_acl.go:204.50,206.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:208.2,208.50 1 1 +github.com/echovault/echovault/echovault/api_acl.go:208.50,210.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:212.2,212.47 1 1 +github.com/echovault/echovault/echovault/api_acl.go:212.47,214.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:216.2,216.47 1 1 +github.com/echovault/echovault/echovault/api_acl.go:216.47,218.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:220.2,220.48 1 1 +github.com/echovault/echovault/echovault/api_acl.go:220.48,222.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:224.2,224.43 1 1 +github.com/echovault/echovault/echovault/api_acl.go:224.43,226.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:228.2,228.44 1 1 +github.com/echovault/echovault/echovault/api_acl.go:228.44,230.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:232.2,232.47 1 1 +github.com/echovault/echovault/echovault/api_acl.go:232.47,234.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:236.2,236.47 1 1 +github.com/echovault/echovault/echovault/api_acl.go:236.47,238.3 1 1 +github.com/echovault/echovault/echovault/api_acl.go:240.2,241.16 2 1 +github.com/echovault/echovault/echovault/api_acl.go:241.16,243.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:245.2,246.40 2 1 +github.com/echovault/echovault/echovault/api_acl.go:293.83,295.16 2 1 +github.com/echovault/echovault/echovault/api_acl.go:295.16,297.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:299.2,301.16 3 1 +github.com/echovault/echovault/echovault/api_acl.go:301.16,303.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:305.2,309.35 3 1 +github.com/echovault/echovault/echovault/api_acl.go:309.35,315.35 4 1 +github.com/echovault/echovault/echovault/api_acl.go:315.35,317.4 1 1 +github.com/echovault/echovault/echovault/api_acl.go:320.2,320.20 1 1 +github.com/echovault/echovault/echovault/api_acl.go:330.72,333.16 3 1 +github.com/echovault/echovault/echovault/api_acl.go:333.16,335.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:336.2,337.40 2 1 +github.com/echovault/echovault/echovault/api_acl.go:341.54,343.16 2 1 +github.com/echovault/echovault/echovault/api_acl.go:343.16,345.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:346.2,346.45 1 1 +github.com/echovault/echovault/echovault/api_acl.go:357.72,359.9 2 0 +github.com/echovault/echovault/echovault/api_acl.go:360.21,361.29 1 0 +github.com/echovault/echovault/echovault/api_acl.go:362.23,363.31 1 0 +github.com/echovault/echovault/echovault/api_acl.go:364.10,365.31 1 0 +github.com/echovault/echovault/echovault/api_acl.go:368.2,369.16 2 0 +github.com/echovault/echovault/echovault/api_acl.go:369.16,371.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:373.2,374.40 2 0 +github.com/echovault/echovault/echovault/api_acl.go:380.50,382.16 2 0 +github.com/echovault/echovault/echovault/api_acl.go:382.16,384.3 1 0 +github.com/echovault/echovault/echovault/api_acl.go:385.2,386.40 2 0 +github.com/echovault/echovault/echovault/api_admin.go:142.87,145.22 2 1 +github.com/echovault/echovault/echovault/api_admin.go:145.22,146.10 1 1 +github.com/echovault/echovault/echovault/api_admin.go:147.32,148.75 1 1 +github.com/echovault/echovault/echovault/api_admin.go:149.33,150.77 1 1 +github.com/echovault/echovault/echovault/api_admin.go:151.32,152.75 1 1 +github.com/echovault/echovault/echovault/api_admin.go:156.2,157.16 2 1 +github.com/echovault/echovault/echovault/api_admin.go:157.16,159.3 1 0 +github.com/echovault/echovault/echovault/api_admin.go:161.2,161.45 1 1 +github.com/echovault/echovault/echovault/api_admin.go:167.54,169.16 2 1 +github.com/echovault/echovault/echovault/api_admin.go:169.16,171.3 1 0 +github.com/echovault/echovault/echovault/api_admin.go:172.2,172.41 1 1 +github.com/echovault/echovault/echovault/api_admin.go:179.47,181.16 2 1 +github.com/echovault/echovault/echovault/api_admin.go:181.16,183.3 1 0 +github.com/echovault/echovault/echovault/api_admin.go:184.2,185.42 2 1 +github.com/echovault/echovault/echovault/api_admin.go:189.50,191.16 2 1 +github.com/echovault/echovault/echovault/api_admin.go:191.16,193.3 1 0 +github.com/echovault/echovault/echovault/api_admin.go:194.2,194.41 1 1 +github.com/echovault/echovault/echovault/api_admin.go:198.55,200.16 2 0 +github.com/echovault/echovault/echovault/api_admin.go:200.16,202.3 1 0 +github.com/echovault/echovault/echovault/api_admin.go:203.2,203.40 1 0 +github.com/echovault/echovault/echovault/api_admin.go:215.67,219.36 3 1 +github.com/echovault/echovault/echovault/api_admin.go:219.36,220.52 1 1 +github.com/echovault/echovault/echovault/api_admin.go:220.52,222.4 1 0 +github.com/echovault/echovault/echovault/api_admin.go:225.2,225.63 1 1 +github.com/echovault/echovault/echovault/api_admin.go:225.63,230.32 1 1 +github.com/echovault/echovault/echovault/api_admin.go:230.32,233.44 2 1 +github.com/echovault/echovault/echovault/api_admin.go:233.44,235.6 1 0 +github.com/echovault/echovault/echovault/api_admin.go:236.5,236.16 1 1 +github.com/echovault/echovault/echovault/api_admin.go:240.111,242.19 2 0 +github.com/echovault/echovault/echovault/api_admin.go:242.19,244.6 1 0 +github.com/echovault/echovault/echovault/api_admin.go:245.5,249.11 1 0 +github.com/echovault/echovault/echovault/api_admin.go:251.94,259.5 1 1 +github.com/echovault/echovault/echovault/api_admin.go:261.3,261.13 1 1 +github.com/echovault/echovault/echovault/api_admin.go:265.2,268.31 1 1 +github.com/echovault/echovault/echovault/api_admin.go:268.31,271.43 2 1 +github.com/echovault/echovault/echovault/api_admin.go:271.43,273.5 1 0 +github.com/echovault/echovault/echovault/api_admin.go:274.4,274.15 1 1 +github.com/echovault/echovault/echovault/api_admin.go:278.83,280.4 1 0 +github.com/echovault/echovault/echovault/api_admin.go:281.71,281.90 1 0 +github.com/echovault/echovault/echovault/api_admin.go:285.2,285.40 1 1 +github.com/echovault/echovault/echovault/api_admin.go:285.40,287.92 1 1 +github.com/echovault/echovault/echovault/api_admin.go:287.92,289.4 1 1 +github.com/echovault/echovault/echovault/api_admin.go:289.6,290.12 1 0 +github.com/echovault/echovault/echovault/api_admin.go:292.3,295.32 1 1 +github.com/echovault/echovault/echovault/api_admin.go:295.32,298.39 2 1 +github.com/echovault/echovault/echovault/api_admin.go:298.39,300.6 1 0 +github.com/echovault/echovault/echovault/api_admin.go:301.5,301.16 1 1 +github.com/echovault/echovault/echovault/api_admin.go:305.111,307.19 2 0 +github.com/echovault/echovault/echovault/api_admin.go:307.19,309.6 1 0 +github.com/echovault/echovault/echovault/api_admin.go:310.5,314.11 1 0 +github.com/echovault/echovault/echovault/api_admin.go:316.94,324.5 1 1 +github.com/echovault/echovault/echovault/api_admin.go:328.2,330.12 2 1 +github.com/echovault/echovault/echovault/api_admin.go:356.76,358.2 1 1 +github.com/echovault/echovault/echovault/api_admin.go:374.59,378.22 3 1 +github.com/echovault/echovault/echovault/api_admin.go:379.9,381.86 1 1 +github.com/echovault/echovault/echovault/api_admin.go:381.86,383.4 1 1 +github.com/echovault/echovault/echovault/api_admin.go:384.9,386.45 1 1 +github.com/echovault/echovault/echovault/api_admin.go:386.45,387.66 1 1 +github.com/echovault/echovault/echovault/api_admin.go:387.66,388.13 1 1 +github.com/echovault/echovault/echovault/api_admin.go:390.4,390.88 1 1 +github.com/echovault/echovault/echovault/api_admin.go:390.88,391.122 1 1 +github.com/echovault/echovault/echovault/api_admin.go:391.122,393.6 1 1 +github.com/echovault/echovault/echovault/api_generic.go:88.91,91.9 2 1 +github.com/echovault/echovault/echovault/api_generic.go:92.18,93.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:94.18,95.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:98.2,98.9 1 1 +github.com/echovault/echovault/echovault/api_generic.go:99.23,100.65 1 1 +github.com/echovault/echovault/echovault/api_generic.go:101.23,102.65 1 1 +github.com/echovault/echovault/echovault/api_generic.go:103.25,104.69 1 1 +github.com/echovault/echovault/echovault/api_generic.go:105.25,106.69 1 1 +github.com/echovault/echovault/echovault/api_generic.go:109.2,109.17 1 1 +github.com/echovault/echovault/echovault/api_generic.go:109.17,111.3 1 1 +github.com/echovault/echovault/echovault/api_generic.go:113.2,114.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:114.16,116.3 1 1 +github.com/echovault/echovault/echovault/api_generic.go:118.2,119.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:119.16,121.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:122.2,122.18 1 1 +github.com/echovault/echovault/echovault/api_generic.go:122.18,124.3 1 1 +github.com/echovault/echovault/echovault/api_generic.go:126.2,126.33 1 1 +github.com/echovault/echovault/echovault/api_generic.go:141.72,144.28 2 1 +github.com/echovault/echovault/echovault/api_generic.go:144.28,146.3 1 1 +github.com/echovault/echovault/echovault/api_generic.go:148.2,149.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:149.16,151.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:153.2,154.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:154.16,156.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:158.2,158.40 1 1 +github.com/echovault/echovault/echovault/api_generic.go:169.58,171.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:171.16,173.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:174.2,174.40 1 1 +github.com/echovault/echovault/echovault/api_generic.go:185.65,187.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:187.16,189.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:190.2,190.45 1 1 +github.com/echovault/echovault/echovault/api_generic.go:200.59,202.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:202.16,204.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:205.2,205.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:216.60,218.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:218.16,220.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:221.2,221.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:231.62,233.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:233.16,235.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:236.2,236.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:246.63,248.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:248.16,250.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:251.2,251.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:261.55,263.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:263.16,265.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:266.2,266.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:276.56,278.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:278.16,280.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:281.2,281.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:296.95,299.9 2 1 +github.com/echovault/echovault/echovault/api_generic.go:300.18,301.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:302.18,303.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:304.18,305.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:306.18,307.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:310.2,311.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:311.16,313.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:315.2,315.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:330.102,333.9 2 1 +github.com/echovault/echovault/echovault/api_generic.go:334.18,335.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:336.18,337.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:338.18,339.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:340.18,341.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:344.2,345.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:345.16,347.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:349.2,349.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:364.102,367.9 2 1 +github.com/echovault/echovault/echovault/api_generic.go:368.18,369.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:370.18,371.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:372.18,373.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:374.18,375.26 1 1 +github.com/echovault/echovault/echovault/api_generic.go:378.2,379.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:379.16,381.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:383.2,383.41 1 1 +github.com/echovault/echovault/echovault/api_generic.go:398.109,401.9 2 1 +github.com/echovault/echovault/echovault/api_generic.go:402.18,403.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:404.18,405.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:406.18,407.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:408.18,409.26 1 0 +github.com/echovault/echovault/echovault/api_generic.go:412.2,413.16 2 1 +github.com/echovault/echovault/echovault/api_generic.go:413.16,415.3 1 0 +github.com/echovault/echovault/echovault/api_generic.go:417.2,417.41 1 1 +github.com/echovault/echovault/echovault/api_hash.go:46.91,49.36 2 1 +github.com/echovault/echovault/echovault/api_hash.go:49.36,51.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:53.2,54.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:54.16,56.3 1 0 +github.com/echovault/echovault/echovault/api_hash.go:58.2,58.41 1 1 +github.com/echovault/echovault/echovault/api_hash.go:76.93,79.36 2 1 +github.com/echovault/echovault/echovault/api_hash.go:79.36,81.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:83.2,84.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:84.16,86.3 1 0 +github.com/echovault/echovault/echovault/api_hash.go:88.2,88.41 1 1 +github.com/echovault/echovault/echovault/api_hash.go:104.79,112.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:112.16,114.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:115.2,115.45 1 1 +github.com/echovault/echovault/echovault/api_hash.go:132.79,136.16 3 1 +github.com/echovault/echovault/echovault/api_hash.go:136.16,138.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:140.2,140.46 1 1 +github.com/echovault/echovault/echovault/api_hash.go:154.62,156.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:156.16,158.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:159.2,159.45 1 1 +github.com/echovault/echovault/echovault/api_hash.go:175.94,178.24 2 1 +github.com/echovault/echovault/echovault/api_hash.go:178.24,180.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:180.8,182.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:184.2,184.24 1 1 +github.com/echovault/echovault/echovault/api_hash.go:184.24,186.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:188.2,189.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:189.16,191.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:193.2,193.45 1 1 +github.com/echovault/echovault/echovault/api_hash.go:207.56,209.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:209.16,211.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:212.2,212.41 1 1 +github.com/echovault/echovault/echovault/api_hash.go:226.62,228.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:228.16,230.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:231.2,231.45 1 1 +github.com/echovault/echovault/echovault/api_hash.go:250.85,252.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:252.16,254.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:255.2,255.39 1 1 +github.com/echovault/echovault/echovault/api_hash.go:259.94,261.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:261.16,263.3 1 0 +github.com/echovault/echovault/echovault/api_hash.go:264.2,264.39 1 1 +github.com/echovault/echovault/echovault/api_hash.go:279.64,281.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:281.16,283.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:284.2,284.45 1 1 +github.com/echovault/echovault/echovault/api_hash.go:300.67,302.16 2 1 +github.com/echovault/echovault/echovault/api_hash.go:302.16,304.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:305.2,305.41 1 1 +github.com/echovault/echovault/echovault/api_hash.go:321.74,324.16 3 1 +github.com/echovault/echovault/echovault/api_hash.go:324.16,326.3 1 1 +github.com/echovault/echovault/echovault/api_hash.go:327.2,327.41 1 1 +github.com/echovault/echovault/echovault/api_list.go:34.56,36.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:36.16,38.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:39.2,39.41 1 1 +github.com/echovault/echovault/echovault/api_list.go:62.79,64.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:64.16,66.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:67.2,67.45 1 1 +github.com/echovault/echovault/echovault/api_list.go:85.73,87.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:87.16,89.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:90.2,90.40 1 1 +github.com/echovault/echovault/echovault/api_list.go:110.82,112.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:112.16,114.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:115.2,116.40 2 1 +github.com/echovault/echovault/echovault/api_list.go:123.78,125.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:125.16,127.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:128.2,129.40 2 1 +github.com/echovault/echovault/echovault/api_list.go:147.82,154.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:154.16,156.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:157.2,158.40 2 1 +github.com/echovault/echovault/echovault/api_list.go:182.94,184.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:184.16,186.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:187.2,188.40 2 1 +github.com/echovault/echovault/echovault/api_list.go:202.59,204.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:204.16,206.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:207.2,207.40 1 1 +github.com/echovault/echovault/echovault/api_list.go:221.59,223.16 2 1 +github.com/echovault/echovault/echovault/api_list.go:223.16,225.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:226.2,226.40 1 1 +github.com/echovault/echovault/echovault/api_list.go:243.75,246.16 3 1 +github.com/echovault/echovault/echovault/api_list.go:246.16,248.3 1 0 +github.com/echovault/echovault/echovault/api_list.go:249.2,249.41 1 1 +github.com/echovault/echovault/echovault/api_list.go:265.76,268.16 3 1 +github.com/echovault/echovault/echovault/api_list.go:268.16,270.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:271.2,271.41 1 1 +github.com/echovault/echovault/echovault/api_list.go:288.75,291.16 3 1 +github.com/echovault/echovault/echovault/api_list.go:291.16,293.3 1 0 +github.com/echovault/echovault/echovault/api_list.go:294.2,294.41 1 1 +github.com/echovault/echovault/echovault/api_list.go:310.76,313.16 3 1 +github.com/echovault/echovault/echovault/api_list.go:313.16,315.3 1 1 +github.com/echovault/echovault/echovault/api_list.go:316.2,316.41 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:42.69,46.41 3 1 +github.com/echovault/echovault/echovault/api_pubsub.go:46.41,55.3 4 1 +github.com/echovault/echovault/echovault/api_pubsub.go:55.8,58.10 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:58.10,60.4 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:61.3,62.33 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:65.2,65.33 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:78.95,80.16 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:80.16,81.26 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:81.26,83.4 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:87.2,88.12 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:88.12,90.3 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:92.2,92.25 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:92.25,97.33 4 1 +github.com/echovault/echovault/echovault/api_pubsub.go:97.33,99.4 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:101.3,101.13 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:112.70,114.9 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:114.9,116.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:117.2,118.107 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:131.96,133.16 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:133.16,134.26 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:134.26,136.4 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:140.2,141.12 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:141.12,143.3 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:145.2,145.25 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:145.25,150.33 4 1 +github.com/echovault/echovault/echovault/api_pubsub.go:150.33,152.4 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:154.3,154.13 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:165.71,167.9 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:167.9,169.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:170.2,171.107 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:184.73,186.16 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:186.16,188.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:189.2,190.40 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:200.75,202.19 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:202.19,204.3 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:205.2,206.16 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:206.16,208.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:209.2,209.45 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:215.54,217.16 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:217.16,219.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:220.2,220.41 1 1 +github.com/echovault/echovault/echovault/api_pubsub.go:230.83,234.16 3 1 +github.com/echovault/echovault/echovault/api_pubsub.go:234.16,236.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:238.2,240.16 3 1 +github.com/echovault/echovault/echovault/api_pubsub.go:240.16,242.3 1 0 +github.com/echovault/echovault/echovault/api_pubsub.go:244.2,247.28 3 1 +github.com/echovault/echovault/echovault/api_pubsub.go:247.28,250.3 2 1 +github.com/echovault/echovault/echovault/api_pubsub.go:252.2,252.20 1 1 +github.com/echovault/echovault/echovault/api_set.go:36.75,39.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:39.16,41.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:42.2,42.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:56.57,58.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:58.16,60.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:61.2,61.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:78.66,81.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:81.16,83.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:84.2,84.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:91.86,94.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:94.16,96.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:97.2,97.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:114.67,117.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:117.16,119.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:120.2,120.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:138.77,140.15 2 1 +github.com/echovault/echovault/echovault/api_set.go:140.15,142.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:143.2,144.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:144.16,146.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:147.2,147.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:152.87,155.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:155.16,157.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:158.2,158.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:174.70,176.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:176.16,178.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:179.2,179.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:193.65,195.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:195.16,197.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:198.2,198.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:215.84,218.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:218.16,220.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:221.2,221.46 1 1 +github.com/echovault/echovault/echovault/api_set.go:243.82,245.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:245.16,247.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:248.2,248.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:264.73,266.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:266.16,268.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:269.2,269.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:286.79,288.16 2 1 +github.com/echovault/echovault/echovault/api_set.go:288.16,290.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:291.2,291.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:307.75,310.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:310.16,312.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:313.2,313.41 1 1 +github.com/echovault/echovault/echovault/api_set.go:328.67,331.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:331.16,333.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:334.2,334.45 1 1 +github.com/echovault/echovault/echovault/api_set.go:341.87,344.16 3 1 +github.com/echovault/echovault/echovault/api_set.go:344.16,346.3 1 1 +github.com/echovault/echovault/echovault/api_set.go:347.2,347.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:105.87,107.28 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:107.28,108.17 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:108.17,110.18 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:110.18,112.5 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:113.4,114.12 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:116.3,116.23 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:118.2,118.20 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:142.105,145.9 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:146.18,147.26 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:148.18,149.26 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:152.2,152.9 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:153.18,154.26 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:155.18,156.26 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:159.2,159.16 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:159.16,161.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:163.2,163.18 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:163.18,165.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:167.2,167.37 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:167.37,169.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:171.2,172.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:172.16,174.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:176.2,176.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:190.57,192.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:192.16,194.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:195.2,195.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:213.76,221.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:221.16,223.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:224.2,224.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:242.93,244.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:244.16,246.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:247.2,248.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:248.16,250.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:252.2,253.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:253.16,255.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:257.2,257.45 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:274.86,277.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:277.16,279.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:280.2,280.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:297.99,300.30 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:300.30,302.45 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:302.45,304.4 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:307.2,307.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:307.29,309.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:311.2,311.24 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:311.24,313.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:315.2,316.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:316.16,318.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:320.2,321.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:321.16,323.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:325.2,325.53 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:344.114,347.30 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:347.30,349.42 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:349.42,351.4 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:354.2,354.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:354.29,356.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:358.2,358.24 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:358.24,360.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:362.2,363.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:363.16,365.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:367.2,367.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:384.99,387.30 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:387.30,389.42 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:389.42,391.4 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:394.2,394.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:394.29,396.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:398.2,398.24 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:398.24,400.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:402.2,403.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:403.16,405.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:407.2,408.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:408.16,410.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:412.2,412.53 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:431.114,434.30 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:434.30,436.42 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:436.42,438.4 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:441.2,441.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:441.29,443.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:445.2,445.24 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:445.24,447.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:449.2,450.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:450.16,452.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:454.2,454.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:473.97,476.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:476.16,478.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:479.2,480.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:480.16,482.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:483.2,483.15 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:500.89,503.9 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:504.19,505.27 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:506.19,507.27 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:508.10,509.27 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:512.2,512.9 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:513.26,514.76 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:515.10,516.59 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:519.2,520.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:520.16,522.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:524.2,524.51 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:542.88,544.33 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:544.33,546.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:548.2,549.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:549.16,551.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:553.2,554.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:554.16,556.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:558.2,559.24 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:559.24,560.14 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:560.14,562.12 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:564.3,565.17 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:565.17,567.4 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:568.3,568.20 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:571.2,571.20 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:591.71,594.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:594.16,596.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:597.2,597.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:616.78,618.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:618.16,620.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:621.2,621.51 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:640.78,642.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:642.16,644.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:645.2,645.51 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:669.98,671.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:671.16,673.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:674.2,674.16 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:674.16,676.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:678.2,679.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:679.16,681.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:683.2,683.51 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:704.101,706.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:706.16,708.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:710.2,711.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:711.16,713.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:715.2,717.19 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:717.19,719.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:721.2,722.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:722.16,724.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:726.2,728.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:728.16,730.17 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:730.17,732.4 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:733.3,733.13 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:736.2,736.17 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:741.104,743.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:743.16,745.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:747.2,748.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:748.16,750.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:752.2,754.19 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:754.19,756.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:758.2,759.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:759.16,761.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:763.2,765.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:765.16,767.17 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:767.17,769.4 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:770.3,770.13 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:773.2,773.17 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:790.81,793.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:793.16,795.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:797.2,798.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:798.16,800.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:802.2,802.11 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:802.11,804.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:806.2,807.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:807.16,809.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:811.2,811.19 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:827.75,829.33 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:829.33,831.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:832.2,833.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:833.16,835.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:836.2,836.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:854.94,863.16 3 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:863.16,865.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:867.2,867.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:885.76,892.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:892.16,894.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:895.2,895.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:913.81,920.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:920.16,922.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:923.2,923.41 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:943.109,946.9 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:947.23,948.31 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:949.21,950.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:951.19,952.27 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:953.10,954.31 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:957.2,957.24 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:957.24,959.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:961.2,961.47 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:961.47,963.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:965.2,966.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:966.16,968.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:970.2,971.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:971.16,973.3 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:975.2,975.53 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:997.120,1000.9 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1001.23,1002.31 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1003.21,1004.29 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1005.19,1006.27 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:1007.10,1008.31 1 0 +github.com/echovault/echovault/echovault/api_sorted_set.go:1011.2,1011.47 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1011.47,1013.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1015.2,1016.16 2 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1016.16,1018.3 1 1 +github.com/echovault/echovault/echovault/api_sorted_set.go:1020.2,1020.41 1 1 +github.com/echovault/echovault/echovault/api_string.go:30.84,32.16 2 1 +github.com/echovault/echovault/echovault/api_string.go:32.16,34.3 1 0 +github.com/echovault/echovault/echovault/api_string.go:35.2,35.41 1 1 +github.com/echovault/echovault/echovault/api_string.go:45.58,47.16 2 1 +github.com/echovault/echovault/echovault/api_string.go:47.16,49.3 1 0 +github.com/echovault/echovault/echovault/api_string.go:50.2,50.41 1 1 +github.com/echovault/echovault/echovault/api_string.go:63.77,65.16 2 1 +github.com/echovault/echovault/echovault/api_string.go:65.16,67.3 1 0 +github.com/echovault/echovault/echovault/api_string.go:68.2,68.40 1 1 +github.com/echovault/echovault/echovault/api_string.go:72.79,74.16 2 1 +github.com/echovault/echovault/echovault/api_string.go:74.16,76.3 1 0 +github.com/echovault/echovault/echovault/api_string.go:77.2,77.40 1 1 +github.com/echovault/echovault/echovault/cluster.go:25.45,27.2 1 1 +github.com/echovault/echovault/echovault/cluster.go:29.84,40.16 4 1 +github.com/echovault/echovault/echovault/cluster.go:40.16,42.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:44.2,46.43 2 1 +github.com/echovault/echovault/echovault/cluster.go:46.43,48.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:50.2,52.9 2 1 +github.com/echovault/echovault/echovault/cluster.go:52.9,54.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:56.2,56.20 1 1 +github.com/echovault/echovault/echovault/cluster.go:56.20,58.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:60.2,60.12 1 1 +github.com/echovault/echovault/echovault/cluster.go:63.94,75.16 5 1 +github.com/echovault/echovault/echovault/cluster.go:75.16,77.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:79.2,81.43 2 1 +github.com/echovault/echovault/echovault/cluster.go:81.43,83.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:85.2,87.9 2 1 +github.com/echovault/echovault/echovault/cluster.go:87.9,89.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:91.2,91.20 1 1 +github.com/echovault/echovault/echovault/cluster.go:91.20,93.3 1 0 +github.com/echovault/echovault/echovault/cluster.go:95.2,95.24 1 1 +github.com/echovault/echovault/echovault/config.go:23.36,25.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:109.66,110.36 1 1 +github.com/echovault/echovault/echovault/echovault.go:110.36,112.3 1 1 +github.com/echovault/echovault/echovault/echovault.go:118.66,119.36 1 1 +github.com/echovault/echovault/echovault/echovault.go:119.36,121.3 1 1 +github.com/echovault/echovault/echovault/echovault.go:126.78,134.39 1 1 +github.com/echovault/echovault/echovault/echovault.go:134.39,147.4 12 1 +github.com/echovault/echovault/echovault/echovault.go:151.2,151.33 1 1 +github.com/echovault/echovault/echovault/echovault.go:151.33,153.3 1 1 +github.com/echovault/echovault/echovault/echovault.go:155.2,161.48 2 1 +github.com/echovault/echovault/echovault/echovault.go:161.48,162.52 1 0 +github.com/echovault/echovault/echovault/echovault.go:162.52,164.12 2 0 +github.com/echovault/echovault/echovault/echovault.go:166.3,166.41 1 0 +github.com/echovault/echovault/echovault/echovault.go:170.2,175.29 3 1 +github.com/echovault/echovault/echovault/echovault.go:175.29,185.38 1 1 +github.com/echovault/echovault/echovault/echovault.go:185.38,189.5 3 1 +github.com/echovault/echovault/echovault/echovault.go:190.49,192.44 2 0 +github.com/echovault/echovault/echovault/echovault.go:192.44,193.46 1 0 +github.com/echovault/echovault/echovault/echovault.go:193.46,195.7 1 0 +github.com/echovault/echovault/echovault/echovault.go:197.5,197.17 1 0 +github.com/echovault/echovault/echovault/echovault.go:200.3,208.5 1 1 +github.com/echovault/echovault/echovault/echovault.go:209.8,220.65 1 1 +github.com/echovault/echovault/echovault/echovault.go:220.65,222.44 2 1 +github.com/echovault/echovault/echovault/echovault.go:222.44,223.46 1 0 +github.com/echovault/echovault/echovault/echovault.go:223.46,225.7 1 0 +github.com/echovault/echovault/echovault/echovault.go:227.5,227.17 1 1 +github.com/echovault/echovault/echovault/echovault.go:229.72,231.93 2 0 +github.com/echovault/echovault/echovault/echovault.go:231.93,233.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:234.5,234.56 1 0 +github.com/echovault/echovault/echovault/echovault.go:238.3,244.60 1 1 +github.com/echovault/echovault/echovault/echovault.go:244.60,246.44 2 0 +github.com/echovault/echovault/echovault/echovault.go:246.44,247.46 1 0 +github.com/echovault/echovault/echovault/echovault.go:247.46,249.7 1 0 +github.com/echovault/echovault/echovault/echovault.go:251.5,251.17 1 0 +github.com/echovault/echovault/echovault/echovault.go:253.68,255.94 2 0 +github.com/echovault/echovault/echovault/echovault.go:255.94,257.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:258.5,258.57 1 0 +github.com/echovault/echovault/echovault/echovault.go:260.51,262.19 2 0 +github.com/echovault/echovault/echovault/echovault.go:262.19,264.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:267.3,267.17 1 1 +github.com/echovault/echovault/echovault/echovault.go:267.17,269.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:270.3,270.34 1 1 +github.com/echovault/echovault/echovault/echovault.go:274.2,274.61 1 1 +github.com/echovault/echovault/echovault/echovault.go:274.61,275.13 1 1 +github.com/echovault/echovault/echovault/echovault.go:275.13,276.8 1 1 +github.com/echovault/echovault/echovault/echovault.go:276.8,278.83 2 1 +github.com/echovault/echovault/echovault/echovault.go:278.83,280.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:285.2,285.69 1 1 +github.com/echovault/echovault/echovault/echovault.go:285.69,287.3 1 0 +github.com/echovault/echovault/echovault/echovault.go:289.2,289.29 1 1 +github.com/echovault/echovault/echovault/echovault.go:289.29,293.36 3 1 +github.com/echovault/echovault/echovault/echovault.go:293.36,295.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:298.2,298.30 1 1 +github.com/echovault/echovault/echovault/echovault.go:298.30,301.34 2 1 +github.com/echovault/echovault/echovault/echovault.go:301.34,303.18 2 0 +github.com/echovault/echovault/echovault/echovault.go:303.18,305.5 1 0 +github.com/echovault/echovault/echovault/echovault.go:309.3,309.71 1 1 +github.com/echovault/echovault/echovault/echovault.go:309.71,311.18 2 0 +github.com/echovault/echovault/echovault/echovault.go:311.18,313.5 1 0 +github.com/echovault/echovault/echovault/echovault.go:317.2,317.23 1 1 +github.com/echovault/echovault/echovault/echovault.go:320.37,332.16 4 1 +github.com/echovault/echovault/echovault/echovault.go:332.16,335.3 2 0 +github.com/echovault/echovault/echovault/echovault.go:337.2,337.15 1 1 +github.com/echovault/echovault/echovault/echovault.go:337.15,340.3 1 1 +github.com/echovault/echovault/echovault/echovault.go:342.2,342.27 1 1 +github.com/echovault/echovault/echovault/echovault.go:342.27,344.15 1 1 +github.com/echovault/echovault/echovault/echovault.go:344.15,346.4 1 1 +github.com/echovault/echovault/echovault/echovault.go:346.9,348.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:350.3,351.49 2 1 +github.com/echovault/echovault/echovault/echovault.go:351.49,353.18 2 1 +github.com/echovault/echovault/echovault/echovault.go:353.18,356.5 2 0 +github.com/echovault/echovault/echovault/echovault.go:357.4,357.42 1 1 +github.com/echovault/echovault/echovault/echovault.go:360.3,363.16 3 1 +github.com/echovault/echovault/echovault/echovault.go:363.16,365.37 2 1 +github.com/echovault/echovault/echovault/echovault.go:365.37,367.19 2 1 +github.com/echovault/echovault/echovault/echovault.go:367.19,370.6 2 0 +github.com/echovault/echovault/echovault/echovault.go:371.5,372.19 2 1 +github.com/echovault/echovault/echovault/echovault.go:372.19,374.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:375.5,375.61 1 1 +github.com/echovault/echovault/echovault/echovault.go:375.61,377.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:381.3,385.5 1 1 +github.com/echovault/echovault/echovault/echovault.go:388.2,391.6 2 1 +github.com/echovault/echovault/echovault/echovault.go:391.6,392.10 1 1 +github.com/echovault/echovault/echovault/echovault.go:393.22,394.10 1 1 +github.com/echovault/echovault/echovault/echovault.go:395.11,397.18 2 1 +github.com/echovault/echovault/echovault/echovault.go:397.18,399.13 2 1 +github.com/echovault/echovault/echovault/echovault.go:402.4,402.36 1 1 +github.com/echovault/echovault/echovault/echovault.go:407.58,409.23 1 1 +github.com/echovault/echovault/echovault/echovault.go:409.23,411.3 1 1 +github.com/echovault/echovault/echovault/echovault.go:413.2,419.15 4 1 +github.com/echovault/echovault/echovault/echovault.go:419.15,421.38 2 1 +github.com/echovault/echovault/echovault/echovault.go:421.38,423.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:426.2,426.6 1 1 +github.com/echovault/echovault/echovault/echovault.go:426.6,429.43 2 1 +github.com/echovault/echovault/echovault/echovault.go:429.43,432.9 2 0 +github.com/echovault/echovault/echovault/echovault.go:435.3,435.17 1 1 +github.com/echovault/echovault/echovault/echovault.go:435.17,437.9 2 0 +github.com/echovault/echovault/echovault/echovault.go:440.3,441.43 2 1 +github.com/echovault/echovault/echovault/echovault.go:441.43,442.9 1 1 +github.com/echovault/echovault/echovault/echovault.go:444.3,444.17 1 1 +github.com/echovault/echovault/echovault/echovault.go:444.17,445.87 1 0 +github.com/echovault/echovault/echovault/echovault.go:445.87,447.5 1 0 +github.com/echovault/echovault/echovault/echovault.go:448.4,448.12 1 0 +github.com/echovault/echovault/echovault/echovault.go:451.3,454.20 2 1 +github.com/echovault/echovault/echovault/echovault.go:454.20,455.12 1 0 +github.com/echovault/echovault/echovault/echovault.go:458.3,458.28 1 1 +github.com/echovault/echovault/echovault/echovault.go:458.28,460.12 2 1 +github.com/echovault/echovault/echovault/echovault.go:464.3,465.7 2 0 +github.com/echovault/echovault/echovault/echovault.go:465.7,467.41 1 0 +github.com/echovault/echovault/echovault/echovault.go:467.41,469.19 2 0 +github.com/echovault/echovault/echovault/echovault.go:469.19,471.6 1 0 +github.com/echovault/echovault/echovault/echovault.go:472.5,472.10 1 0 +github.com/echovault/echovault/echovault/echovault.go:474.4,475.21 2 0 +github.com/echovault/echovault/echovault/echovault.go:475.21,476.10 1 0 +github.com/echovault/echovault/echovault/echovault.go:478.4,478.27 1 0 +github.com/echovault/echovault/echovault/echovault.go:488.34,490.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:493.47,494.38 1 1 +github.com/echovault/echovault/echovault/echovault.go:494.38,496.3 1 0 +github.com/echovault/echovault/echovault/echovault.go:498.2,498.12 1 1 +github.com/echovault/echovault/echovault/echovault.go:498.12,499.27 1 1 +github.com/echovault/echovault/echovault/echovault.go:499.27,501.53 1 0 +github.com/echovault/echovault/echovault/echovault.go:501.53,503.5 1 0 +github.com/echovault/echovault/echovault/echovault.go:504.4,504.10 1 0 +github.com/echovault/echovault/echovault/echovault.go:507.3,507.62 1 1 +github.com/echovault/echovault/echovault/echovault.go:507.62,509.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:512.2,512.12 1 1 +github.com/echovault/echovault/echovault/echovault.go:515.42,517.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:519.43,521.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:523.56,525.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:528.56,530.2 1 1 +github.com/echovault/echovault/echovault/echovault.go:532.44,534.2 1 0 +github.com/echovault/echovault/echovault/echovault.go:536.45,538.2 1 0 +github.com/echovault/echovault/echovault/echovault.go:541.45,542.40 1 0 +github.com/echovault/echovault/echovault/echovault.go:542.40,544.3 1 0 +github.com/echovault/echovault/echovault/echovault.go:545.2,545.12 1 0 +github.com/echovault/echovault/echovault/echovault.go:545.12,546.55 1 0 +github.com/echovault/echovault/echovault/echovault.go:546.55,548.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:550.2,550.12 1 0 +github.com/echovault/echovault/echovault/echovault.go:555.37,556.28 1 1 +github.com/echovault/echovault/echovault/echovault.go:556.28,557.13 1 1 +github.com/echovault/echovault/echovault/echovault.go:557.13,557.42 1 1 +github.com/echovault/echovault/echovault/echovault.go:558.3,559.49 2 1 +github.com/echovault/echovault/echovault/echovault.go:559.49,561.4 1 0 +github.com/echovault/echovault/echovault/echovault.go:563.2,563.26 1 1 +github.com/echovault/echovault/echovault/echovault.go:563.26,566.3 2 1 +github.com/echovault/echovault/echovault/echovault.go:569.45,586.2 2 1 +github.com/echovault/echovault/echovault/keyspace.go:32.67,38.27 4 1 +github.com/echovault/echovault/echovault/keyspace.go:38.27,41.3 2 1 +github.com/echovault/echovault/echovault/keyspace.go:43.2,43.15 1 1 +github.com/echovault/echovault/echovault/keyspace.go:46.58,51.9 4 1 +github.com/echovault/echovault/echovault/keyspace.go:51.9,53.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:55.2,55.23 1 1 +github.com/echovault/echovault/echovault/keyspace.go:58.95,64.27 4 1 +github.com/echovault/echovault/echovault/keyspace.go:64.27,66.10 2 1 +github.com/echovault/echovault/echovault/keyspace.go:66.10,68.12 2 1 +github.com/echovault/echovault/echovault/keyspace.go:71.3,71.83 1 1 +github.com/echovault/echovault/echovault/keyspace.go:71.83,72.29 1 0 +github.com/echovault/echovault/echovault/keyspace.go:72.29,75.19 2 0 +github.com/echovault/echovault/echovault/keyspace.go:75.19,77.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:78.10,78.65 1 0 +github.com/echovault/echovault/echovault/keyspace.go:78.65,81.19 2 0 +github.com/echovault/echovault/echovault/keyspace.go:81.19,83.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:84.10,84.66 1 0 +github.com/echovault/echovault/echovault/keyspace.go:84.66,89.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:90.4,91.12 2 0 +github.com/echovault/echovault/echovault/keyspace.go:94.3,94.28 1 1 +github.com/echovault/echovault/echovault/keyspace.go:98.2,98.46 1 1 +github.com/echovault/echovault/echovault/keyspace.go:98.46,99.61 1 1 +github.com/echovault/echovault/echovault/keyspace.go:99.61,101.4 1 0 +github.com/echovault/echovault/echovault/keyspace.go:104.2,104.15 1 1 +github.com/echovault/echovault/echovault/keyspace.go:107.95,111.115 3 1 +github.com/echovault/echovault/echovault/keyspace.go:111.115,113.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:115.2,115.34 1 1 +github.com/echovault/echovault/echovault/keyspace.go:115.34,117.37 2 1 +github.com/echovault/echovault/echovault/keyspace.go:117.37,119.4 1 1 +github.com/echovault/echovault/echovault/keyspace.go:120.3,124.28 2 1 +github.com/echovault/echovault/echovault/keyspace.go:124.28,126.4 1 1 +github.com/echovault/echovault/echovault/keyspace.go:130.2,130.63 1 1 +github.com/echovault/echovault/echovault/keyspace.go:130.63,131.31 1 1 +github.com/echovault/echovault/echovault/keyspace.go:131.31,133.18 2 1 +github.com/echovault/echovault/echovault/keyspace.go:133.18,135.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:139.2,139.12 1 1 +github.com/echovault/echovault/echovault/keyspace.go:142.101,153.55 5 1 +github.com/echovault/echovault/echovault/keyspace.go:153.55,155.3 1 1 +github.com/echovault/echovault/echovault/keyspace.go:156.2,159.11 2 1 +github.com/echovault/echovault/echovault/keyspace.go:159.11,160.44 1 1 +github.com/echovault/echovault/echovault/keyspace.go:160.44,162.18 2 1 +github.com/echovault/echovault/echovault/keyspace.go:162.18,164.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:169.54,176.97 4 1 +github.com/echovault/echovault/echovault/keyspace.go:176.97,178.3 1 1 +github.com/echovault/echovault/echovault/keyspace.go:181.2,181.9 1 1 +github.com/echovault/echovault/echovault/keyspace.go:182.108,183.36 1 0 +github.com/echovault/echovault/echovault/keyspace.go:184.108,185.36 1 0 +github.com/echovault/echovault/echovault/keyspace.go:188.2,190.12 2 1 +github.com/echovault/echovault/echovault/keyspace.go:193.60,195.6 1 1 +github.com/echovault/echovault/echovault/keyspace.go:195.6,196.83 1 1 +github.com/echovault/echovault/echovault/keyspace.go:196.83,198.9 2 1 +github.com/echovault/echovault/echovault/keyspace.go:201.2,202.33 2 1 +github.com/echovault/echovault/echovault/keyspace.go:202.33,204.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:205.2,206.13 2 1 +github.com/echovault/echovault/echovault/keyspace.go:211.86,212.27 1 1 +github.com/echovault/echovault/echovault/keyspace.go:212.27,214.84 1 1 +github.com/echovault/echovault/echovault/keyspace.go:214.84,216.4 1 1 +github.com/echovault/echovault/echovault/keyspace.go:218.3,218.35 1 1 +github.com/echovault/echovault/echovault/keyspace.go:218.35,220.4 1 1 +github.com/echovault/echovault/echovault/keyspace.go:221.3,221.56 1 0 +github.com/echovault/echovault/echovault/keyspace.go:222.29,225.34 3 0 +github.com/echovault/echovault/echovault/keyspace.go:226.29,229.34 3 0 +github.com/echovault/echovault/echovault/keyspace.go:230.30,232.51 2 0 +github.com/echovault/echovault/echovault/keyspace.go:232.51,234.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:235.4,235.34 1 0 +github.com/echovault/echovault/echovault/keyspace.go:236.30,238.51 2 0 +github.com/echovault/echovault/echovault/keyspace.go:238.51,240.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:241.4,241.34 1 0 +github.com/echovault/echovault/echovault/keyspace.go:243.3,243.55 1 0 +github.com/echovault/echovault/echovault/keyspace.go:243.55,245.4 1 0 +github.com/echovault/echovault/echovault/keyspace.go:247.2,247.12 1 0 +github.com/echovault/echovault/echovault/keyspace.go:251.71,253.34 1 0 +github.com/echovault/echovault/echovault/keyspace.go:253.34,255.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:258.2,261.50 3 0 +github.com/echovault/echovault/echovault/keyspace.go:261.50,263.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:265.2,267.50 3 0 +github.com/echovault/echovault/echovault/keyspace.go:267.50,269.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:273.2,275.9 3 0 +github.com/echovault/echovault/echovault/keyspace.go:276.125,281.7 3 0 +github.com/echovault/echovault/echovault/keyspace.go:281.7,283.40 1 0 +github.com/echovault/echovault/echovault/keyspace.go:283.40,285.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:287.4,288.29 2 0 +github.com/echovault/echovault/echovault/keyspace.go:288.29,290.49 1 0 +github.com/echovault/echovault/echovault/keyspace.go:290.49,292.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:293.10,293.65 1 0 +github.com/echovault/echovault/echovault/keyspace.go:293.65,295.63 1 0 +github.com/echovault/echovault/echovault/keyspace.go:295.63,297.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:301.4,304.52 3 0 +github.com/echovault/echovault/echovault/keyspace.go:304.52,306.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:308.125,313.7 3 0 +github.com/echovault/echovault/echovault/keyspace.go:313.7,315.40 1 0 +github.com/echovault/echovault/echovault/keyspace.go:315.40,317.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:319.4,320.29 2 0 +github.com/echovault/echovault/echovault/keyspace.go:320.29,322.49 1 0 +github.com/echovault/echovault/echovault/keyspace.go:322.49,324.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:325.10,325.65 1 0 +github.com/echovault/echovault/echovault/keyspace.go:325.65,328.63 1 0 +github.com/echovault/echovault/echovault/keyspace.go:328.63,330.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:334.4,337.52 3 0 +github.com/echovault/echovault/echovault/keyspace.go:337.52,339.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:341.105,344.7 1 0 +github.com/echovault/echovault/echovault/keyspace.go:344.7,347.30 2 0 +github.com/echovault/echovault/echovault/keyspace.go:347.30,351.5 3 0 +github.com/echovault/echovault/echovault/keyspace.go:353.4,354.37 2 0 +github.com/echovault/echovault/echovault/keyspace.go:354.37,355.17 1 0 +github.com/echovault/echovault/echovault/keyspace.go:355.17,356.31 1 0 +github.com/echovault/echovault/echovault/keyspace.go:356.31,358.51 1 0 +github.com/echovault/echovault/echovault/keyspace.go:358.51,360.8 1 0 +github.com/echovault/echovault/echovault/keyspace.go:361.12,361.67 1 0 +github.com/echovault/echovault/echovault/keyspace.go:361.67,362.65 1 0 +github.com/echovault/echovault/echovault/keyspace.go:362.65,364.8 1 0 +github.com/echovault/echovault/echovault/keyspace.go:367.6,370.54 3 0 +github.com/echovault/echovault/echovault/keyspace.go:370.54,372.7 1 0 +github.com/echovault/echovault/echovault/keyspace.go:374.5,374.10 1 0 +github.com/echovault/echovault/echovault/keyspace.go:377.106,380.7 1 0 +github.com/echovault/echovault/echovault/keyspace.go:380.7,387.29 5 0 +github.com/echovault/echovault/echovault/keyspace.go:387.29,389.49 1 0 +github.com/echovault/echovault/echovault/keyspace.go:389.49,391.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:392.10,392.65 1 0 +github.com/echovault/echovault/echovault/keyspace.go:392.65,393.63 1 0 +github.com/echovault/echovault/echovault/keyspace.go:393.63,395.6 1 0 +github.com/echovault/echovault/echovault/keyspace.go:399.4,402.52 3 0 +github.com/echovault/echovault/echovault/keyspace.go:402.52,404.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:406.10,407.13 1 0 +github.com/echovault/echovault/echovault/keyspace.go:416.77,418.57 1 1 +github.com/echovault/echovault/echovault/keyspace.go:418.57,420.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:422.2,427.50 3 1 +github.com/echovault/echovault/echovault/keyspace.go:427.50,429.3 1 0 +github.com/echovault/echovault/echovault/keyspace.go:430.2,437.33 6 1 +github.com/echovault/echovault/echovault/keyspace.go:437.33,438.7 1 0 +github.com/echovault/echovault/echovault/keyspace.go:438.7,442.35 3 0 +github.com/echovault/echovault/echovault/keyspace.go:442.35,444.10 2 0 +github.com/echovault/echovault/echovault/keyspace.go:448.2,453.25 4 1 +github.com/echovault/echovault/echovault/keyspace.go:453.25,456.28 2 0 +github.com/echovault/echovault/echovault/keyspace.go:456.28,457.46 1 0 +github.com/echovault/echovault/echovault/keyspace.go:457.46,459.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:460.9,460.64 1 0 +github.com/echovault/echovault/echovault/keyspace.go:460.64,461.60 1 0 +github.com/echovault/echovault/echovault/keyspace.go:461.60,463.5 1 0 +github.com/echovault/echovault/echovault/keyspace.go:468.2,468.21 1 1 +github.com/echovault/echovault/echovault/keyspace.go:468.21,470.3 1 1 +github.com/echovault/echovault/echovault/keyspace.go:472.2,475.58 2 0 +github.com/echovault/echovault/echovault/keyspace.go:475.58,479.3 2 0 +github.com/echovault/echovault/echovault/keyspace.go:481.2,481.12 1 0 +github.com/echovault/echovault/echovault/modules.go:29.75,32.42 3 1 +github.com/echovault/echovault/echovault/modules.go:32.42,33.46 1 1 +github.com/echovault/echovault/echovault/modules.go:33.46,35.4 1 1 +github.com/echovault/echovault/echovault/modules.go:37.2,37.72 1 1 +github.com/echovault/echovault/echovault/modules.go:40.125,60.37 1 1 +github.com/echovault/echovault/echovault/modules.go:60.37,64.4 3 1 +github.com/echovault/echovault/echovault/modules.go:68.137,70.16 2 1 +github.com/echovault/echovault/echovault/modules.go:70.16,72.3 1 1 +github.com/echovault/echovault/echovault/modules.go:74.2,74.19 1 1 +github.com/echovault/echovault/echovault/modules.go:74.19,76.3 1 0 +github.com/echovault/echovault/echovault/modules.go:79.2,79.39 1 1 +github.com/echovault/echovault/echovault/modules.go:79.39,81.3 1 0 +github.com/echovault/echovault/echovault/modules.go:83.2,84.16 2 1 +github.com/echovault/echovault/echovault/modules.go:84.16,86.3 1 1 +github.com/echovault/echovault/echovault/modules.go:88.2,92.16 4 1 +github.com/echovault/echovault/echovault/modules.go:92.16,94.3 1 1 +github.com/echovault/echovault/echovault/modules.go:95.2,96.8 2 1 +github.com/echovault/echovault/echovault/modules.go:96.8,99.3 2 1 +github.com/echovault/echovault/echovault/modules.go:101.2,101.51 1 1 +github.com/echovault/echovault/echovault/modules.go:101.51,104.87 1 1 +github.com/echovault/echovault/echovault/modules.go:104.87,106.4 1 0 +github.com/echovault/echovault/echovault/modules.go:110.2,110.50 1 1 +github.com/echovault/echovault/echovault/modules.go:110.50,111.7 1 1 +github.com/echovault/echovault/echovault/modules.go:111.7,112.42 1 1 +github.com/echovault/echovault/echovault/modules.go:112.42,114.10 2 1 +github.com/echovault/echovault/echovault/modules.go:119.2,119.43 1 1 +github.com/echovault/echovault/echovault/modules.go:119.43,121.17 2 1 +github.com/echovault/echovault/echovault/modules.go:121.17,123.4 1 1 +github.com/echovault/echovault/echovault/modules.go:125.3,125.62 1 1 +github.com/echovault/echovault/echovault/modules.go:125.62,127.4 1 1 +github.com/echovault/echovault/echovault/modules.go:129.3,131.18 2 1 +github.com/echovault/echovault/echovault/modules.go:135.2,135.32 1 1 +github.com/echovault/echovault/echovault/modules.go:135.32,138.17 3 1 +github.com/echovault/echovault/echovault/modules.go:138.17,140.4 1 0 +github.com/echovault/echovault/echovault/modules.go:141.3,141.18 1 1 +github.com/echovault/echovault/echovault/modules.go:145.2,145.34 1 1 +github.com/echovault/echovault/echovault/modules.go:145.34,148.3 2 1 +github.com/echovault/echovault/echovault/modules.go:150.2,150.72 1 0 +github.com/echovault/echovault/echovault/modules.go:153.59,155.2 1 1 +github.com/echovault/echovault/echovault/modules.go:157.47,159.2 1 1 +github.com/echovault/echovault/echovault/modules.go:161.50,163.2 1 1 +github.com/echovault/echovault/echovault/modules.go:165.49,167.2 1 1 +github.com/echovault/echovault/echovault/plugin.go:37.72,41.41 3 1 +github.com/echovault/echovault/echovault/plugin.go:41.41,42.37 1 1 +github.com/echovault/echovault/echovault/plugin.go:42.37,44.4 1 1 +github.com/echovault/echovault/echovault/plugin.go:45.3,45.44 1 0 +github.com/echovault/echovault/echovault/plugin.go:48.2,49.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:49.16,51.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:53.2,54.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:54.16,56.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:57.2,58.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:58.9,60.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:62.2,63.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:63.16,65.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:66.2,67.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:67.9,69.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:71.2,72.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:72.16,74.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:75.2,76.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:76.9,78.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:80.2,81.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:81.16,83.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:84.2,85.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:85.9,87.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:89.2,90.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:90.16,92.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:93.2,94.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:94.9,96.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:98.2,99.16 2 1 +github.com/echovault/echovault/echovault/plugin.go:99.16,101.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:102.2,110.9 2 1 +github.com/echovault/echovault/echovault/plugin.go:110.9,112.3 1 0 +github.com/echovault/echovault/echovault/plugin.go:115.2,115.91 1 1 +github.com/echovault/echovault/echovault/plugin.go:115.91,117.3 1 1 +github.com/echovault/echovault/echovault/plugin.go:120.2,123.31 1 1 +github.com/echovault/echovault/echovault/plugin.go:123.31,126.36 2 1 +github.com/echovault/echovault/echovault/plugin.go:126.36,128.5 1 1 +github.com/echovault/echovault/echovault/plugin.go:129.4,129.15 1 1 +github.com/echovault/echovault/echovault/plugin.go:134.83,136.18 2 0 +github.com/echovault/echovault/echovault/plugin.go:136.18,138.5 1 0 +github.com/echovault/echovault/echovault/plugin.go:139.4,143.10 1 0 +github.com/echovault/echovault/echovault/plugin.go:145.72,154.4 1 1 +github.com/echovault/echovault/echovault/plugin.go:157.2,157.12 1 1 +github.com/echovault/echovault/echovault/plugin.go:165.54,168.91 3 1 +github.com/echovault/echovault/echovault/plugin.go:168.91,170.3 1 1 +github.com/echovault/echovault/echovault/plugin.go:176.49,180.42 4 1 +github.com/echovault/echovault/echovault/plugin.go:180.42,181.61 1 1 +github.com/echovault/echovault/echovault/plugin.go:181.61,183.4 1 1 +github.com/echovault/echovault/echovault/plugin.go:183.6,185.4 1 1 +github.com/echovault/echovault/echovault/plugin.go:187.2,187.16 1 1 +github.com/echovault/echovault/echovault/test_helpers.go:9.35,16.2 2 1 +github.com/echovault/echovault/echovault/test_helpers.go:18.63,23.2 2 1 +github.com/echovault/echovault/echovault/test_helpers.go:25.95,26.82 1 1 +github.com/echovault/echovault/echovault/test_helpers.go:26.82,28.3 1 0 +github.com/echovault/echovault/echovault/test_helpers.go:29.2,29.12 1 1 +github.com/echovault/echovault/echovault/test_helpers.go:32.95,35.2 2 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:34.51,35.32 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:35.32,37.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:41.57,42.32 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:42.32,45.3 2 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:48.61,59.33 3 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:59.33,61.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:63.2,63.16 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:66.28,67.12 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:67.12,68.7 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:68.7,73.40 3 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:73.40,74.30 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:74.30,79.21 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:79.21,81.7 1 0 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:85.4,85.33 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:90.34,92.2 1 0 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:94.40,96.2 1 0 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:98.51,101.40 3 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:101.40,103.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:104.2,105.11 2 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:108.53,111.40 3 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:111.40,113.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:114.2,115.13 2 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:118.44,120.2 1 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:122.36,129.2 4 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:131.34,138.2 4 1 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:140.59,145.35 4 0 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:145.35,147.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/channel.go:149.2,149.20 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:25.73,27.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:27.9,29.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:31.2,33.24 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:33.24,35.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:37.2,40.17 3 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:43.75,45.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:45.9,47.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:49.2,53.90 3 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:56.71,58.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:58.9,60.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:61.2,61.30 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:61.30,63.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:64.2,65.42 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:68.78,69.29 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:69.29,71.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:73.2,74.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:74.9,76.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:78.2,79.30 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:79.30,81.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:83.2,83.38 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:86.76,88.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:88.9,90.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:91.2,92.49 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:95.77,97.9 2 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:97.9,99.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:100.2,100.47 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:103.36,111.84 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:111.84,113.21 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:113.21,115.6 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:116.5,120.11 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:130.84,132.21 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:132.21,134.6 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:135.5,139.11 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:149.84,151.22 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:151.22,153.6 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:154.5,158.11 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:170.84,177.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:188.84,194.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:203.84,209.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:210.68,212.5 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:222.86,228.7 1 0 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:237.86,243.7 1 1 +github.com/echovault/echovault/internal/modules/pubsub/commands.go:253.86,259.7 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:33.26,38.2 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:40.101,47.17 5 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:47.17,49.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:51.2,51.37 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:51.37,55.75 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:55.75,57.4 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:59.3,59.23 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:59.23,62.19 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:62.19,64.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:64.10,66.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:67.4,68.31 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:68.31,73.20 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:73.20,75.6 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:76.5,76.47 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:78.9,80.47 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:80.47,85.20 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:85.20,87.6 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:93.110,98.17 4 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:98.17,100.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:102.2,105.24 3 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:105.24,106.19 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:106.19,109.40 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:109.40,110.31 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:110.31,111.14 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:113.5,113.34 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:113.34,116.6 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:118.9,121.40 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:121.40,122.31 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:122.31,123.14 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:125.5,125.34 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:125.34,128.6 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:136.2,136.38 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:136.38,137.30 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:137.30,138.54 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:138.54,141.5 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:147.2,147.17 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:147.17,148.36 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:148.36,150.40 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:150.40,152.58 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:152.58,153.35 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:153.35,156.7 2 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:157.6,157.14 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:160.5,160.30 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:160.30,161.35 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:161.35,164.7 2 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:170.2,171.39 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:171.39,173.3 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:175.2,175.20 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:178.82,182.38 3 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:182.38,184.29 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:184.29,185.35 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:185.35,187.5 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:188.4,188.12 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:191.3,191.41 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:191.41,193.4 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:197.51,204.19 5 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:204.19,205.39 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:205.39,206.26 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:206.26,209.5 2 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:211.3,212.21 2 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:215.2,217.38 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:217.38,219.78 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:219.78,222.12 3 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:225.3,225.50 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:225.50,228.4 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:231.2,231.53 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:234.32,239.38 4 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:239.38,240.51 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:240.51,242.4 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:244.2,244.14 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:247.52,252.35 4 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:252.35,254.66 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:254.66,256.4 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:257.3,257.20 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:257.20,259.12 2 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:261.3,261.106 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:263.2,263.20 1 1 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:266.47,271.38 4 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:271.38,273.3 1 0 +github.com/echovault/echovault/internal/modules/pubsub/pubsub.go:275.2,275.17 1 0 diff --git a/echovault/echovault_test.go b/echovault/echovault_test.go index f8899c0..075b3f1 100644 --- a/echovault/echovault_test.go +++ b/echovault/echovault_test.go @@ -40,6 +40,7 @@ type ClientServerPair struct { raftPort int mlPort int bootstrapCluster bool + joinAddr string raw net.Conn client *resp.Conn server *EchoVault @@ -92,9 +93,57 @@ func setupServer( ) } +func setupNode(node *ClientServerPair, isLeader bool, errChan *chan error) { + server, err := setupServer( + node.serverId, + node.bootstrapCluster, + node.bindAddr, + node.joinAddr, + node.port, + node.raftPort, + node.mlPort, + ) + if err != nil { + *errChan <- fmt.Errorf("could not start server; %v", err) + } + + // Start the server. + go func() { + server.Start() + }() + + if isLeader { + // If node is a leader, wait until it's established itself as a leader of the raft cluster. + for { + if server.raft.IsRaftLeader() { + break + } + } + } else { + // If the node is a follower, wait until it's joined the raft cluster before moving forward. + for { + if server.raft.HasJoinedCluster() { + break + } + } + } + + // Setup client connection. + conn, err := internal.GetConnection(node.bindAddr, node.port) + if err != nil { + *errChan <- fmt.Errorf("could not open tcp connection: %v", err) + } + client := resp.NewConn(conn) + + node.raw = conn + node.client = client + node.server = server +} + func makeCluster(size int) ([]ClientServerPair, error) { pairs := make([]ClientServerPair, size) + // Set up node metadata. for i := 0; i < len(pairs); i++ { serverId := fmt.Sprintf("SERVER-%d", i) bindAddr := getBindAddr().String() @@ -115,42 +164,6 @@ func makeCluster(size int) ([]ClientServerPair, error) { if err != nil { return nil, fmt.Errorf("could not get free memberlist port: %v", err) } - server, err := setupServer(serverId, bootstrapCluster, bindAddr, joinAddr, port, raftPort, memberlistPort) - if err != nil { - return nil, fmt.Errorf("could not start server; %v", err) - } - - // Start the server - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - server.Start() - }() - wg.Wait() - - if i == 0 { - // If node is a leader, wait until it's established itself as a leader of the raft cluster. - for { - if server.raft.IsRaftLeader() { - break - } - } - } else { - // If the node is a follower, wait until it's joined the raft cluster before moving forward. - for { - if server.raft.HasJoinedCluster() { - break - } - } - } - - // Setup client connection. - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", bindAddr, port)) - if err != nil { - return nil, fmt.Errorf("could not open tcp connection: %v", err) - } - client := resp.NewConn(conn) pairs[i] = ClientServerPair{ serverId: serverId, @@ -159,12 +172,37 @@ func makeCluster(size int) ([]ClientServerPair, error) { raftPort: raftPort, mlPort: memberlistPort, bootstrapCluster: bootstrapCluster, - raw: conn, - client: client, - server: server, + joinAddr: joinAddr, } } + errChan := make(chan error) + doneChan := make(chan struct{}) + + // Set up nodes. + wg := sync.WaitGroup{} + for i := 0; i < len(pairs); i++ { + if i == 0 { + setupNode(&pairs[i], pairs[i].bootstrapCluster, &errChan) + continue + } + wg.Add(1) + go func(idx int) { + setupNode(&pairs[idx], pairs[idx].bootstrapCluster, &errChan) + wg.Done() + }(i) + } + go func() { + wg.Wait() + doneChan <- struct{}{} + }() + + select { + case err := <-errChan: + return nil, err + case <-doneChan: + } + return pairs, nil } @@ -428,275 +466,283 @@ func Test_Cluster(t *testing.T) { } }) - // t.Run("Test_ForwardCommand", func(t *testing.T) { - // tests := tests["forward"] - // // Write all the data a random cluster follower. - // for i, test := range tests { - // // Send write command to follower node. - // node := nodes[1] - // if err := node.client.WriteArray([]resp.Value{ - // resp.StringValue("SET"), - // resp.StringValue(test.key), - // resp.StringValue(test.value), - // }); err != nil { - // t.Errorf("could not write data to follower node (test %d): %v", i, err) - // } - // // Read response and make sure we received "ok" response. - // rd, _, err := node.client.ReadValue() - // if err != nil { - // t.Errorf("could not read response from follower node (test %d): %v", i, err) - // } - // if !strings.EqualFold(rd.String(), "ok") { - // t.Errorf("expected response for test %d to be \"OK\", got %s", i, rd.String()) - // } - // } - // - // <-time.After(200 * time.Millisecond) // Short yield to allow change to take effect. - // - // // Check if the data has been replicated on a quorum (majority of the cluster). - // quorum := int(math.Ceil(float64(len(nodes)/2)) + 1) - // for i, test := range tests { - // count := 0 - // for j := 0; j < len(nodes); j++ { - // node := nodes[j] - // if err := node.client.WriteArray([]resp.Value{ - // resp.StringValue("GET"), - // resp.StringValue(test.key), - // }); err != nil { - // t.Errorf("could not write data to follower node %d (test %d): %v", j, i, err) - // } - // rd, _, err := node.client.ReadValue() - // if err != nil { - // t.Errorf("could not read data from follower node %d (test %d): %v", j, i, err) - // } - // if rd.String() == test.value { - // count += 1 // If the expected value is found, increment the count. - // } - // } - // // Fail if count is less than quorum. - // if count < quorum { - // t.Errorf("could not find value %s at key %s in cluster quorum", test.value, test.key) - // } - // } - // }) + t.Run("Test_ForwardCommand", func(t *testing.T) { + tests := tests["forward"] + // Write all the data a random cluster follower. + for i, test := range tests { + // Send write command to follower node. + node := nodes[1] + if err := node.client.WriteArray([]resp.Value{ + resp.StringValue("SET"), + resp.StringValue(test.key), + resp.StringValue(test.value), + }); err != nil { + t.Errorf("could not write data to follower node (test %d): %v", i, err) + } + // Read response and make sure we received "ok" response. + rd, _, err := node.client.ReadValue() + if err != nil { + t.Errorf("could not read response from follower node (test %d): %v", i, err) + } + if !strings.EqualFold(rd.String(), "ok") { + t.Errorf("expected response for test %d to be \"OK\", got %s", i, rd.String()) + } + } + + <-time.After(1 * time.Second) // Short yield to allow change to take effect. + + // Check if the data has been replicated on a quorum (majority of the cluster). + quorum := int(math.Ceil(float64(len(nodes)/2)) + 1) + for i, test := range tests { + count := 0 + for j := 0; j < len(nodes); j++ { + node := nodes[j] + if err := node.client.WriteArray([]resp.Value{ + resp.StringValue("GET"), + resp.StringValue(test.key), + }); err != nil { + t.Errorf("could not write data to follower node %d (test %d): %v", j, i, err) + } + rd, _, err := node.client.ReadValue() + if err != nil { + t.Errorf("could not read data from follower node %d (test %d): %v", j, i, err) + } + if rd.String() == test.value { + count += 1 // If the expected value is found, increment the count. + } + } + // Fail if count is less than quorum. + if count < quorum { + t.Errorf("could not find value %s at key %s in cluster quorum", test.value, test.key) + } + } + }) } -func Test_TLS(t *testing.T) { - port, err := internal.GetFreePort() - if err != nil { - t.Error(err) - } +func Test_Standalone(t *testing.T) { + t.Run("Test_TLS", func(t *testing.T) { + t.Parallel() - conf := DefaultConfig() - conf.DataDir = "" - conf.BindAddr = "localhost" - conf.Port = uint16(port) - conf.TLS = true - conf.CertKeyPairs = [][]string{ - { - path.Join("..", "openssl", "server", "server1.crt"), - path.Join("..", "openssl", "server", "server1.key"), - }, - { - path.Join("..", "openssl", "server", "server2.crt"), - path.Join("..", "openssl", "server", "server2.key"), - }, - } + port, err := internal.GetFreePort() + if err != nil { + t.Error(err) + return + } - server, err := NewEchoVault(WithConfig(conf)) - if err != nil { - t.Error(err) - return - } + conf := DefaultConfig() + conf.DataDir = "" + conf.BindAddr = "localhost" + conf.Port = uint16(port) + conf.TLS = true + conf.CertKeyPairs = [][]string{ + { + path.Join("..", "openssl", "server", "server1.crt"), + path.Join("..", "openssl", "server", "server1.key"), + }, + { + path.Join("..", "openssl", "server", "server2.crt"), + path.Join("..", "openssl", "server", "server2.key"), + }, + } - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - server.Start() - }() - wg.Wait() + server, err := NewEchoVault(WithConfig(conf)) + if err != nil { + t.Error(err) + return + } - // Dial with ServerCAs - serverCAs := x509.NewCertPool() - f, err := os.Open(path.Join("..", "openssl", "server", "rootCA.crt")) - if err != nil { - t.Error(err) - } - cert, err := io.ReadAll(bufio.NewReader(f)) - if err != nil { - t.Error(err) - } - ok := serverCAs.AppendCertsFromPEM(cert) - if !ok { - t.Error("could not load server CA") - } + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + wg.Done() + server.Start() + }() + wg.Wait() - conn, err := tls.Dial("tcp", fmt.Sprintf("localhost:%d", port), &tls.Config{ - RootCAs: serverCAs, - }) - if err != nil { - t.Error(err) - return - } - defer func() { - _ = conn.Close() - server.ShutDown() - }() - client := resp.NewConn(conn) - - // Test that we can set and get a value from the server. - key := "key1" - value := "value1" - err = client.WriteArray([]resp.Value{ - resp.StringValue("SET"), resp.StringValue(key), resp.StringValue(value), - }) - if err != nil { - t.Error(err) - } - - res, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - - if !strings.EqualFold(res.String(), "ok") { - t.Errorf("expected response OK, got \"%s\"", res.String()) - } - - err = client.WriteArray([]resp.Value{resp.StringValue("GET"), resp.StringValue(key)}) - if err != nil { - t.Error(err) - } - - res, _, err = client.ReadValue() - if err != nil { - t.Error(err) - } - - if res.String() != value { - t.Errorf("expected response at key \"%s\" to be \"%s\", got \"%s\"", key, value, res.String()) - } -} - -func Test_MTLS(t *testing.T) { - port, err := internal.GetFreePort() - if err != nil { - t.Error(err) - } - - conf := DefaultConfig() - conf.DataDir = "" - conf.BindAddr = "localhost" - conf.Port = uint16(port) - conf.TLS = true - conf.MTLS = true - conf.ClientCAs = []string{ - path.Join("..", "openssl", "client", "rootCA.crt"), - } - conf.CertKeyPairs = [][]string{ - { - path.Join("..", "openssl", "server", "server1.crt"), - path.Join("..", "openssl", "server", "server1.key"), - }, - { - path.Join("..", "openssl", "server", "server2.crt"), - path.Join("..", "openssl", "server", "server2.key"), - }, - } - - server, err := NewEchoVault(WithConfig(conf)) - if err != nil { - t.Error(err) - return - } - - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - server.Start() - }() - wg.Wait() - - // Dial with ServerCAs and client certificates - clientCertKeyPairs := [][]string{ - { - path.Join("..", "openssl", "client", "client1.crt"), - path.Join("..", "openssl", "client", "client1.key"), - }, - { - path.Join("..", "openssl", "client", "client2.crt"), - path.Join("..", "openssl", "client", "client2.key"), - }, - } - var certificates []tls.Certificate - for _, pair := range clientCertKeyPairs { - c, err := tls.LoadX509KeyPair(pair[0], pair[1]) + // Dial with ServerCAs + serverCAs := x509.NewCertPool() + f, err := os.Open(path.Join("..", "openssl", "server", "rootCA.crt")) if err != nil { t.Error(err) } - certificates = append(certificates, c) - } + cert, err := io.ReadAll(bufio.NewReader(f)) + if err != nil { + t.Error(err) + } + ok := serverCAs.AppendCertsFromPEM(cert) + if !ok { + t.Error("could not load server CA") + } - serverCAs := x509.NewCertPool() - f, err := os.Open(path.Join("..", "openssl", "server", "rootCA.crt")) - if err != nil { - t.Error(err) - } - cert, err := io.ReadAll(bufio.NewReader(f)) - if err != nil { - t.Error(err) - } - ok := serverCAs.AppendCertsFromPEM(cert) - if !ok { - t.Error("could not load server CA") - } + conn, err := internal.GetTLSConnection("localhost", port, &tls.Config{ + RootCAs: serverCAs, + }) + if err != nil { + t.Error(err) + return + } + defer func() { + _ = conn.Close() + server.ShutDown() + }() + client := resp.NewConn(conn) - conn, err := tls.Dial("tcp", fmt.Sprintf("localhost:%d", port), &tls.Config{ - RootCAs: serverCAs, - Certificates: certificates, + // Test that we can set and get a value from the server. + key := "key1" + value := "value1" + err = client.WriteArray([]resp.Value{ + resp.StringValue("SET"), resp.StringValue(key), resp.StringValue(value), + }) + if err != nil { + t.Error(err) + } + + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + + if !strings.EqualFold(res.String(), "ok") { + t.Errorf("expected response OK, got \"%s\"", res.String()) + } + + err = client.WriteArray([]resp.Value{resp.StringValue("GET"), resp.StringValue(key)}) + if err != nil { + t.Error(err) + } + + res, _, err = client.ReadValue() + if err != nil { + t.Error(err) + } + + if res.String() != value { + t.Errorf("expected response at key \"%s\" to be \"%s\", got \"%s\"", key, value, res.String()) + } }) - if err != nil { - t.Error(err) - return - } - defer func() { - _ = conn.Close() - server.ShutDown() - }() - client := resp.NewConn(conn) - // Test that we can set and get a value from the server. - key := "key1" - value := "value1" - err = client.WriteArray([]resp.Value{ - resp.StringValue("SET"), resp.StringValue(key), resp.StringValue(value), + t.Run("Test_MTLS", func(t *testing.T) { + t.Parallel() + + port, err := internal.GetFreePort() + if err != nil { + t.Error(err) + return + } + + conf := DefaultConfig() + conf.DataDir = "" + conf.BindAddr = "localhost" + conf.Port = uint16(port) + conf.TLS = true + conf.MTLS = true + conf.ClientCAs = []string{ + path.Join("..", "openssl", "client", "rootCA.crt"), + } + conf.CertKeyPairs = [][]string{ + { + path.Join("..", "openssl", "server", "server1.crt"), + path.Join("..", "openssl", "server", "server1.key"), + }, + { + path.Join("..", "openssl", "server", "server2.crt"), + path.Join("..", "openssl", "server", "server2.key"), + }, + } + + server, err := NewEchoVault(WithConfig(conf)) + if err != nil { + t.Error(err) + return + } + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + wg.Done() + server.Start() + }() + wg.Wait() + + // Dial with ServerCAs and client certificates + clientCertKeyPairs := [][]string{ + { + path.Join("..", "openssl", "client", "client1.crt"), + path.Join("..", "openssl", "client", "client1.key"), + }, + { + path.Join("..", "openssl", "client", "client2.crt"), + path.Join("..", "openssl", "client", "client2.key"), + }, + } + var certificates []tls.Certificate + for _, pair := range clientCertKeyPairs { + c, err := tls.LoadX509KeyPair(pair[0], pair[1]) + if err != nil { + t.Error(err) + } + certificates = append(certificates, c) + } + + serverCAs := x509.NewCertPool() + f, err := os.Open(path.Join("..", "openssl", "server", "rootCA.crt")) + if err != nil { + t.Error(err) + } + cert, err := io.ReadAll(bufio.NewReader(f)) + if err != nil { + t.Error(err) + } + ok := serverCAs.AppendCertsFromPEM(cert) + if !ok { + t.Error("could not load server CA") + } + + conn, err := internal.GetTLSConnection("localhost", port, &tls.Config{ + RootCAs: serverCAs, + Certificates: certificates, + }) + if err != nil { + t.Error(err) + return + } + defer func() { + _ = conn.Close() + server.ShutDown() + }() + client := resp.NewConn(conn) + + // Test that we can set and get a value from the server. + key := "key1" + value := "value1" + err = client.WriteArray([]resp.Value{ + resp.StringValue("SET"), resp.StringValue(key), resp.StringValue(value), + }) + if err != nil { + t.Error(err) + } + + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + + if !strings.EqualFold(res.String(), "ok") { + t.Errorf("expected response OK, got \"%s\"", res.String()) + } + + err = client.WriteArray([]resp.Value{resp.StringValue("GET"), resp.StringValue(key)}) + if err != nil { + t.Error(err) + } + + res, _, err = client.ReadValue() + if err != nil { + t.Error(err) + } + + if res.String() != value { + t.Errorf("expected response at key \"%s\" to be \"%s\", got \"%s\"", key, value, res.String()) + } }) - if err != nil { - t.Error(err) - } - - res, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - - if !strings.EqualFold(res.String(), "ok") { - t.Errorf("expected response OK, got \"%s\"", res.String()) - } - - err = client.WriteArray([]resp.Value{resp.StringValue("GET"), resp.StringValue(key)}) - if err != nil { - t.Error(err) - } - - res, _, err = client.ReadValue() - if err != nil { - t.Error(err) - } - - if res.String() != value { - t.Errorf("expected response at key \"%s\" to be \"%s\", got \"%s\"", key, value, res.String()) - } } diff --git a/internal/modules/generic/commands_test.go b/internal/modules/generic/commands_test.go index c034ff3..9d091da 100644 --- a/internal/modules/generic/commands_test.go +++ b/internal/modules/generic/commands_test.go @@ -23,9 +23,7 @@ import ( "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/tidwall/resp" - "net" "strings" - "sync" "testing" "time" ) @@ -56,13 +54,9 @@ func Test_Generic(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -70,7 +64,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleSET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -475,7 +469,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleMSET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -567,7 +561,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleGET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -686,7 +680,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleMGET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -793,7 +787,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleDEL", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -905,7 +899,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandlePERSIST", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1062,7 +1056,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleEXPIRETIME", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1183,7 +1177,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleTTL", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1304,7 +1298,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleEXPIRE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1591,7 +1585,7 @@ func Test_Generic(t *testing.T) { t.Run("Test_HandleEXPIREAT", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return diff --git a/internal/modules/hash/commands_test.go b/internal/modules/hash/commands_test.go index e5cbb7a..0cc27f6 100644 --- a/internal/modules/hash/commands_test.go +++ b/internal/modules/hash/commands_test.go @@ -16,17 +16,14 @@ package hash_test import ( "errors" - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/tidwall/resp" - "net" "slices" "strconv" "strings" - "sync" "testing" ) @@ -50,13 +47,9 @@ func Test_Hash(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -64,7 +57,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHSET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -248,7 +241,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHINCRBY", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -450,7 +443,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHGET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -606,7 +599,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHSTRLEN", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -764,7 +757,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHVALS", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -906,7 +899,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHRANDFIELD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1115,7 +1108,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHLEN", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1241,7 +1234,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHKeys", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1369,7 +1362,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHGETALL", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1510,7 +1503,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHEXISTS", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1637,7 +1630,7 @@ func Test_Hash(t *testing.T) { t.Run("Test_HandleHDEL", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return diff --git a/internal/modules/list/commands_test.go b/internal/modules/list/commands_test.go index 1e93f2c..8ff348e 100644 --- a/internal/modules/list/commands_test.go +++ b/internal/modules/list/commands_test.go @@ -16,17 +16,14 @@ package list_test import ( "errors" - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/tidwall/resp" - "net" "slices" "strconv" "strings" - "sync" "testing" ) @@ -50,13 +47,9 @@ func Test_List(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -64,7 +57,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLLEN", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -188,7 +181,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLINDEX", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -352,7 +345,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLRANGE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -541,7 +534,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLSET", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -730,7 +723,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLTRIM", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -930,7 +923,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLREM", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1095,7 +1088,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLMOVE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1326,7 +1319,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleLPUSH", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1481,7 +1474,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandleRPUSH", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -1636,7 +1629,7 @@ func Test_List(t *testing.T) { t.Run("Test_HandlePOP", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return diff --git a/internal/modules/pubsub/commands_test.go b/internal/modules/pubsub/commands_test.go index c551a22..fbf8bf5 100644 --- a/internal/modules/pubsub/commands_test.go +++ b/internal/modules/pubsub/commands_test.go @@ -15,7 +15,6 @@ package pubsub_test import ( - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" @@ -28,777 +27,795 @@ import ( "testing" ) -var mockServer *echovault.EchoVault - -var bindAddr = "localhost" -var port uint16 - -func init() { - p, _ := internal.GetFreePort() - port = uint16(p) - - mockServer = setUpServer(bindAddr, port) - - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - mockServer.Start() - }() - wg.Wait() -} - -func setUpServer(bindAddr string, port uint16) *echovault.EchoVault { - server, _ := echovault.NewEchoVault( +func setUpServer(port int) (*echovault.EchoVault, error) { + return echovault.NewEchoVault( echovault.WithConfig(config.Config{ - BindAddr: bindAddr, - Port: port, + BindAddr: "localhost", + Port: uint16(port), DataDir: "", EvictionPolicy: constants.NoEviction, }), ) - return server } -func Test_HandleSubscribe(t *testing.T) { - numOfConnection := 20 - connections := make([]*resp.Conn, numOfConnection) - for i := 0; i < numOfConnection; i++ { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", bindAddr, port)) - if err != nil { - t.Error(err) - return - } - connections[i] = resp.NewConn(conn) - } - - // Test subscribe to channels - channels := []string{"sub_channel1", "sub_channel2", "sub_channel3"} - command := []resp.Value{resp.StringValue("SUBSCRIBE")} - for _, channel := range channels { - command = append(command, resp.StringValue(channel)) - } - for _, conn := range connections { - if err := conn.WriteArray(command); err != nil { - t.Error(err) - return - } - for i := 0; i < len(channels); i++ { - // Read all the subscription confirmations from the connection. - if _, _, err := conn.ReadValue(); err != nil { - t.Error(err) - return - } - } - } - activeChannels, err := mockServer.PubSubChannels("*") +func Test_PubSub(t *testing.T) { + port, err := internal.GetFreePort() if err != nil { t.Error(err) return } - numSubs, err := mockServer.PubSubNumSub(channels...) + + mockServer, err := setUpServer(port) if err != nil { t.Error(err) return } - for _, channel := range channels { - // Check if the channel exists in the pubsub module. - if !slices.Contains(activeChannels, channel) { - t.Errorf("expected pubsub to contain channel \"%s\" but it was not found", channel) - return - } - // Check if the channel has the right number of subscribers. - if numSubs[channel] != len(connections) { - t.Errorf("expected channel \"%s\" to have %d subscribers, got %d", - channel, len(connections), numSubs[channel]) - return - } - } - // Test subscribe to patterns - patterns := []string{"psub_channel*"} - command = []resp.Value{resp.StringValue("PSUBSCRIBE")} - for _, pattern := range patterns { - command = append(command, resp.StringValue(pattern)) - } - for _, conn := range connections { - if err := conn.WriteArray(command); err != nil { - t.Error(err) - return - } - for i := 0; i < len(patterns); i++ { - // Read all the pattern subscription confirmations from the connection. - if _, _, err := conn.ReadValue(); err != nil { - t.Error(err) - return - } - } - } - numSubs, err = mockServer.PubSubNumSub(patterns...) - if err != nil { - t.Error(err) - return - } - for _, pattern := range patterns { - activePatterns, err := mockServer.PubSubChannels(pattern) - if err != nil { - t.Error(err) - return - } - // Check if pattern channel exists in pubsub module. - if !slices.Contains(activePatterns, pattern) { - t.Errorf("expected pubsub to contain pattern channel \"%s\" but it was not found", pattern) - return - } - // Check if the channel has all the connections from above. - if numSubs[pattern] != len(connections) { - t.Errorf("expected pattern channel \"%s\" to have %d subscribers, got %d", - pattern, len(connections), numSubs[pattern]) - return - } - } -} + go func() { + mockServer.Start() + }() -func Test_HandleUnsubscribe(t *testing.T) { - generateConnections := func(noOfConnections int) []*resp.Conn { - connections := make([]*resp.Conn, noOfConnections) - for i := 0; i < noOfConnections; i++ { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", bindAddr, port)) + t.Cleanup(func() { + mockServer.ShutDown() + }) + + t.Run("Test_HandleSubscribe", func(t *testing.T) { + t.Parallel() + + // Establish connections. + numOfConnections := 20 + rawConnections := make([]net.Conn, numOfConnections) + connections := make([]*resp.Conn, numOfConnections) + for i := 0; i < numOfConnections; i++ { + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) + return } + rawConnections[i] = conn connections[i] = resp.NewConn(conn) } - return connections - } - - verifyResponse := func(res resp.Value, expectedResponse [][]string) { - v := res.Array() - if len(v) != len(expectedResponse) { - t.Errorf("expected subscribe response of length %d, but got %d", len(expectedResponse), len(v)) - } - for _, item := range v { - arr := item.Array() - if len(arr) != 3 { - t.Errorf("expected subscribe response item to be length %d, but got %d", 3, len(arr)) + defer func() { + for _, conn := range rawConnections { + _ = conn.Close() } - if !slices.ContainsFunc(expectedResponse, func(strings []string) bool { - return strings[0] == arr[0].String() && strings[1] == arr[1].String() && strings[2] == arr[2].String() - }) { - t.Errorf("expected to find item \"%s\" in response, did not find it.", arr[1].String()) - } - } - } + }() - tests := []struct { - subChannels []string // All channels to subscribe to - subPatterns []string // All patterns to subscribe to - unSubChannels []string // Channels to unsubscribe from - unSubPatterns []string // Patterns to unsubscribe from - remainChannels []string // Channels to remain subscribed to - remainPatterns []string // Patterns to remain subscribed to - targetConn *resp.Conn // Connection used to test unsubscribe functionality - otherConnections []*resp.Conn // Connections to fill the subscribers list for channels and patterns - expectedResponses map[string][][]string // The expected response from the handler - }{ - { // 1. Unsubscribe from channels and patterns - subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, - subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, - unSubChannels: []string{"xx_channel_one", "xx_channel_two"}, - unSubPatterns: []string{"xx_pattern_[ab]"}, - remainChannels: []string{"xx_channel_three", "xx_channel_four"}, - remainPatterns: []string{"xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, - targetConn: generateConnections(1)[0], - otherConnections: generateConnections(20), - expectedResponses: map[string][][]string{ - "channel": { - {"unsubscribe", "xx_channel_one", "1"}, - {"unsubscribe", "xx_channel_two", "2"}, - }, - "pattern": { - {"punsubscribe", "xx_pattern_[ab]", "1"}, - }, - }, - }, - { // 2. Unsubscribe from all channels no channel or pattern is passed to command - subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, - subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, - unSubChannels: []string{}, - unSubPatterns: []string{}, - remainChannels: []string{}, - remainPatterns: []string{}, - targetConn: generateConnections(1)[0], - otherConnections: generateConnections(20), - expectedResponses: map[string][][]string{ - "channel": { - {"unsubscribe", "xx_channel_one", "1"}, - {"unsubscribe", "xx_channel_two", "2"}, - {"unsubscribe", "xx_channel_three", "3"}, - {"unsubscribe", "xx_channel_four", "4"}, - }, - "pattern": { - {"punsubscribe", "xx_pattern_[ab]", "1"}, - {"punsubscribe", "xx_pattern_[cd]", "2"}, - {"punsubscribe", "xx_pattern_[ef]", "3"}, - {"punsubscribe", "xx_pattern_[gh]", "4"}, - }, - }, - }, - { // 3. Don't unsubscribe from any channels or patterns if the provided ones are non-existent - subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, - subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, - unSubChannels: []string{"xx_channel_non_existent_channel"}, - unSubPatterns: []string{"xx_channel_non_existent_pattern_[ae]"}, - remainChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, - remainPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, - targetConn: generateConnections(1)[0], - otherConnections: generateConnections(20), - expectedResponses: map[string][][]string{ - "channel": {}, - "pattern": {}, - }, - }, - } - - for _, test := range tests { - // Subscribe to channels. - for _, conn := range append(test.otherConnections, test.targetConn) { - command := []resp.Value{resp.StringValue("SUBSCRIBE")} - for _, channel := range test.subChannels { - command = append(command, resp.StringValue(channel)) - } - if err := conn.WriteArray(command); err != nil { - t.Error(err) - return - } - for i := 0; i < len(test.subChannels); i++ { - // Read channel subscription confirmations from connection. - if _, _, err := conn.ReadValue(); err != nil { - t.Error(err) - } - } - - // Subscribe to patterns. - command = []resp.Value{resp.StringValue("PSUBSCRIBE")} - for _, pattern := range test.subPatterns { - command = append(command, resp.StringValue(pattern)) - } - if err := conn.WriteArray(command); err != nil { - t.Error(err) - return - } - for i := 0; i < len(test.subPatterns); i++ { - // Read pattern subscription confirmations from connection. - if _, _, err := conn.ReadValue(); err != nil { - t.Error(err) - } - } - - } - - // Unsubscribe the target connection from the unsub channels. - command := []resp.Value{resp.StringValue("UNSUBSCRIBE")} - for _, channel := range test.unSubChannels { - command = append(command, resp.StringValue(channel)) - } - if err := test.targetConn.WriteArray(command); err != nil { - t.Error(err) - return - } - res, _, err := test.targetConn.ReadValue() - if err != nil { - t.Error(err) - return - } - verifyResponse(res, test.expectedResponses["channel"]) - - // Unsubscribe the target connection from the unsub patterns. - command = []resp.Value{resp.StringValue("PUNSUBSCRIBE")} - for _, pattern := range test.unSubPatterns { - command = append(command, resp.StringValue(pattern)) - } - if err = test.targetConn.WriteArray(command); err != nil { - t.Error(err) - return - } - res, _, err = test.targetConn.ReadValue() - if err != nil { - t.Error(err) - return - } - verifyResponse(res, test.expectedResponses["pattern"]) - } -} - -func Test_HandlePublish(t *testing.T) { - establishConnection := func() *resp.Conn { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", bindAddr, port)) - if err != nil { - t.Error(err) - } - return resp.NewConn(conn) - } - - // verifyChannelMessage reads the message from the connection and asserts whether - // it's the message we expect to read as a subscriber of a channel or pattern. - verifyEvent := func(client *resp.Conn, expected []string) { - rv, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - v := rv.Array() - for i := 0; i < len(v); i++ { - if v[i].String() != expected[i] { - t.Errorf("expected item at index %d to be \"%s\", got \"%s\"", i, expected[i], v[i].String()) - } - } - } - - // The subscribe function handles subscribing the connection to the given - // channels and patterns and reading/verifying the message sent by the server after - // subscription. - subscribe := func(client *resp.Conn, channels []string, patterns []string) { - // Subscribe to channels + // Test subscribe to channels + channels := []string{"sub_channel1", "sub_channel2", "sub_channel3"} command := []resp.Value{resp.StringValue("SUBSCRIBE")} for _, channel := range channels { command = append(command, resp.StringValue(channel)) } - if err := client.WriteArray(command); err != nil { - t.Error(err) - } - for i := 0; i < len(channels); i++ { - // Read channel subscription confirmations. - if _, _, err := client.ReadValue(); err != nil { + for _, conn := range connections { + if err := conn.WriteArray(command); err != nil { t.Error(err) + return + } + for i := 0; i < len(channels); i++ { + // Read all the subscription confirmations from the connection. + if _, _, err := conn.ReadValue(); err != nil { + t.Error(err) + return + } + } + } + activeChannels, err := mockServer.PubSubChannels("*") + if err != nil { + t.Error(err) + return + } + numSubs, err := mockServer.PubSubNumSub(channels...) + if err != nil { + t.Error(err) + return + } + for _, channel := range channels { + // Check if the channel exists in the pubsub module. + if !slices.Contains(activeChannels, channel) { + t.Errorf("expected pubsub to contain channel \"%s\" but it was not found", channel) + return + } + // Check if the channel has the right number of subscribers. + if numSubs[channel] != len(connections) { + t.Errorf("expected channel \"%s\" to have %d subscribers, got %d", + channel, len(connections), numSubs[channel]) + return } } - // Subscribe to all the patterns + // Test subscribe to patterns + patterns := []string{"psub_channel*"} command = []resp.Value{resp.StringValue("PSUBSCRIBE")} for _, pattern := range patterns { command = append(command, resp.StringValue(pattern)) } - if err := client.WriteArray(command); err != nil { - t.Error(err) - } - for i := 0; i < len(patterns); i++ { - // Read pattern subscription confirmations. - if _, _, err := client.ReadValue(); err != nil { + for _, conn := range connections { + if err := conn.WriteArray(command); err != nil { t.Error(err) + return + } + for i := 0; i < len(patterns); i++ { + // Read all the pattern subscription confirmations from the connection. + if _, _, err := conn.ReadValue(); err != nil { + t.Error(err) + return + } } } - } - - subscriptions := []struct { - client *resp.Conn - channels []string - patterns []string - }{ - { - client: establishConnection(), - channels: []string{"pub_channel_1", "pub_channel_2", "pub_channel_3"}, - patterns: []string{"pub_channel_[456]"}, - }, - { - client: establishConnection(), - channels: []string{"pub_channel_6", "pub_channel_7"}, - patterns: []string{"pub_channel_[891]"}, - }, - } - for _, subscription := range subscriptions { - // Subscribe to channels and patterns. - subscribe(subscription.client, subscription.channels, subscription.patterns) - } - - type Subscriber struct { - client *resp.Conn - channel string - } - - tests := []struct { - channel string - message string - subscribers []Subscriber - }{ - { - channel: "pub_channel_1", - message: "Test both subscribers 1", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_1"}, - {client: subscriptions[1].client, channel: "pub_channel_[891]"}, - }, - }, - { - channel: "pub_channel_6", - message: "Test both subscribers 2", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_[456]"}, - {client: subscriptions[1].client, channel: "pub_channel_6"}, - }, - }, - { - channel: "pub_channel_2", - message: "Test subscriber 1 1", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_2"}, - }, - }, - { - channel: "pub_channel_3", - message: "Test subscriber 1 2", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_3"}, - }, - }, - { - channel: "pub_channel_4", - message: "Test both subscribers 2", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_[456]"}, - }, - }, - { - channel: "pub_channel_5", - message: "Test subscriber 1 3", - subscribers: []Subscriber{ - {client: subscriptions[0].client, channel: "pub_channel_[456]"}, - }, - }, - { - channel: "pub_channel_7", - message: "Test subscriber 2 1", - subscribers: []Subscriber{ - {client: subscriptions[1].client, channel: "pub_channel_7"}, - }, - }, - { - channel: "pub_channel_8", - message: "Test subscriber 2 2", - subscribers: []Subscriber{ - {client: subscriptions[1].client, channel: "pub_channel_[891]"}, - }, - }, - { - channel: "pub_channel_9", - message: "Test subscriber 2 3", - subscribers: []Subscriber{ - {client: subscriptions[1].client, channel: "pub_channel_[891]"}, - }, - }, - } - - // Dial echovault to make publisher connection - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", bindAddr, port)) - if err != nil { - t.Error(err) - return - } - publisher := resp.NewConn(conn) - - for _, test := range tests { - err = publisher.WriteArray([]resp.Value{ - resp.StringValue("PUBLISH"), - resp.StringValue(test.channel), - resp.StringValue(test.message), - }) - if err != nil { - t.Error(err) - } - - rv, _, err := publisher.ReadValue() - if err != nil { - t.Error(err) - } - if rv.String() != "OK" { - t.Errorf("Expected publish response to be \"OK\", got \"%s\"", rv.String()) - } - - for _, sub := range test.subscribers { - verifyEvent(sub.client, []string{"message", sub.channel, test.message}) - } - } -} - -func Test_HandlePubSubChannels(t *testing.T) { - verifyExpectedResponse := func(res resp.Value, expected []string) { - if len(res.Array()) != len(expected) { - t.Errorf("expected response array of length %d, got %d", len(expected), len(res.Array())) - } - for _, e := range expected { - if !slices.ContainsFunc(res.Array(), func(v resp.Value) bool { - return e == v.String() - }) { - t.Errorf("expected to find element \"%s\" in response array, could not find it", e) - } - } - } - - port, _ := internal.GetFreePort() - mockServer := setUpServer(bindAddr, uint16(port)) - - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - mockServer.Start() - }() - wg.Wait() - - subscribers := make([]*resp.Conn, 2) - for i := 0; i < len(subscribers); i++ { - conn, err := internal.GetConnection(bindAddr, port) + numSubs, err = mockServer.PubSubNumSub(patterns...) if err != nil { t.Error(err) return } - subscribers[i] = resp.NewConn(conn) - } + for _, pattern := range patterns { + activePatterns, err := mockServer.PubSubChannels(pattern) + if err != nil { + t.Error(err) + return + } + // Check if pattern channel exists in pubsub module. + if !slices.Contains(activePatterns, pattern) { + t.Errorf("expected pubsub to contain pattern channel \"%s\" but it was not found", pattern) + return + } + // Check if the channel has all the connections from above. + if numSubs[pattern] != len(connections) { + t.Errorf("expected pattern channel \"%s\" to have %d subscribers, got %d", + pattern, len(connections), numSubs[pattern]) + return + } + } + }) - channels := []string{"channel_1", "channel_2", "channel_3"} - patterns := []string{"channel_[123]", "channel_[456]"} + t.Run("Test_HandleUnsubscribe", func(t *testing.T) { + t.Parallel() - subscriptions := []struct { - client *resp.Conn - action string - channels []string - patterns []string - }{ - { - client: subscribers[0], - action: "SUBSCRIBE", - channels: channels, - patterns: make([]string, 0), - }, - { - client: subscribers[1], - action: "PSUBSCRIBE", - channels: make([]string, 0), - patterns: patterns, - }, - } - for _, subscription := range subscriptions { - command := []resp.Value{resp.StringValue(subscription.action)} - if len(subscription.channels) > 0 { - for _, channel := range subscription.channels { + var rawConnections []net.Conn + generateConnections := func(noOfConnections int) []*resp.Conn { + connections := make([]*resp.Conn, noOfConnections) + for i := 0; i < noOfConnections; i++ { + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + } + rawConnections = append(rawConnections, conn) + connections[i] = resp.NewConn(conn) + } + return connections + } + defer func() { + for _, conn := range rawConnections { + _ = conn.Close() + } + }() + + verifyResponse := func(res resp.Value, expectedResponse [][]string) { + v := res.Array() + if len(v) != len(expectedResponse) { + t.Errorf("expected subscribe response of length %d, but got %d", len(expectedResponse), len(v)) + } + for _, item := range v { + arr := item.Array() + if len(arr) != 3 { + t.Errorf("expected subscribe response item to be length %d, but got %d", 3, len(arr)) + } + if !slices.ContainsFunc(expectedResponse, func(strings []string) bool { + return strings[0] == arr[0].String() && strings[1] == arr[1].String() && strings[2] == arr[2].String() + }) { + t.Errorf("expected to find item \"%s\" in response, did not find it.", arr[1].String()) + } + } + } + + tests := []struct { + subChannels []string // All channels to subscribe to + subPatterns []string // All patterns to subscribe to + unSubChannels []string // Channels to unsubscribe from + unSubPatterns []string // Patterns to unsubscribe from + remainChannels []string // Channels to remain subscribed to + remainPatterns []string // Patterns to remain subscribed to + targetConn *resp.Conn // Connection used to test unsubscribe functionality + otherConnections []*resp.Conn // Connections to fill the subscribers list for channels and patterns + expectedResponses map[string][][]string // The expected response from the handler + }{ + { // 1. Unsubscribe from channels and patterns + subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, + subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, + unSubChannels: []string{"xx_channel_one", "xx_channel_two"}, + unSubPatterns: []string{"xx_pattern_[ab]"}, + remainChannels: []string{"xx_channel_three", "xx_channel_four"}, + remainPatterns: []string{"xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, + targetConn: generateConnections(1)[0], + otherConnections: generateConnections(20), + expectedResponses: map[string][][]string{ + "channel": { + {"unsubscribe", "xx_channel_one", "1"}, + {"unsubscribe", "xx_channel_two", "2"}, + }, + "pattern": { + {"punsubscribe", "xx_pattern_[ab]", "1"}, + }, + }, + }, + { // 2. Unsubscribe from all channels no channel or pattern is passed to command + subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, + subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, + unSubChannels: []string{}, + unSubPatterns: []string{}, + remainChannels: []string{}, + remainPatterns: []string{}, + targetConn: generateConnections(1)[0], + otherConnections: generateConnections(20), + expectedResponses: map[string][][]string{ + "channel": { + {"unsubscribe", "xx_channel_one", "1"}, + {"unsubscribe", "xx_channel_two", "2"}, + {"unsubscribe", "xx_channel_three", "3"}, + {"unsubscribe", "xx_channel_four", "4"}, + }, + "pattern": { + {"punsubscribe", "xx_pattern_[ab]", "1"}, + {"punsubscribe", "xx_pattern_[cd]", "2"}, + {"punsubscribe", "xx_pattern_[ef]", "3"}, + {"punsubscribe", "xx_pattern_[gh]", "4"}, + }, + }, + }, + { // 3. Don't unsubscribe from any channels or patterns if the provided ones are non-existent + subChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, + subPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, + unSubChannels: []string{"xx_channel_non_existent_channel"}, + unSubPatterns: []string{"xx_channel_non_existent_pattern_[ae]"}, + remainChannels: []string{"xx_channel_one", "xx_channel_two", "xx_channel_three", "xx_channel_four"}, + remainPatterns: []string{"xx_pattern_[ab]", "xx_pattern_[cd]", "xx_pattern_[ef]", "xx_pattern_[gh]"}, + targetConn: generateConnections(1)[0], + otherConnections: generateConnections(20), + expectedResponses: map[string][][]string{ + "channel": {}, + "pattern": {}, + }, + }, + } + + for _, test := range tests { + // Subscribe to channels. + for _, conn := range append(test.otherConnections, test.targetConn) { + command := []resp.Value{resp.StringValue("SUBSCRIBE")} + for _, channel := range test.subChannels { + command = append(command, resp.StringValue(channel)) + } + if err := conn.WriteArray(command); err != nil { + t.Error(err) + return + } + for i := 0; i < len(test.subChannels); i++ { + // Read channel subscription confirmations from connection. + if _, _, err := conn.ReadValue(); err != nil { + t.Error(err) + } + } + + // Subscribe to patterns. + command = []resp.Value{resp.StringValue("PSUBSCRIBE")} + for _, pattern := range test.subPatterns { + command = append(command, resp.StringValue(pattern)) + } + if err := conn.WriteArray(command); err != nil { + t.Error(err) + return + } + for i := 0; i < len(test.subPatterns); i++ { + // Read pattern subscription confirmations from connection. + if _, _, err := conn.ReadValue(); err != nil { + t.Error(err) + } + } + + } + + // Unsubscribe the target connection from the unsub channels. + command := []resp.Value{resp.StringValue("UNSUBSCRIBE")} + for _, channel := range test.unSubChannels { command = append(command, resp.StringValue(channel)) } - } else if len(subscription.patterns) > 0 { - for _, pattern := range subscription.patterns { + if err := test.targetConn.WriteArray(command); err != nil { + t.Error(err) + return + } + res, _, err := test.targetConn.ReadValue() + if err != nil { + t.Error(err) + return + } + verifyResponse(res, test.expectedResponses["channel"]) + + // Unsubscribe the target connection from the unsub patterns. + command = []resp.Value{resp.StringValue("PUNSUBSCRIBE")} + for _, pattern := range test.unSubPatterns { command = append(command, resp.StringValue(pattern)) } - } - if err := subscription.client.WriteArray(command); err != nil { - t.Error(err) - } - if len(subscription.channels) > 0 { - for i := 0; i < len(subscription.channels); i++ { - _, _, _ = subscription.client.ReadValue() + if err = test.targetConn.WriteArray(command); err != nil { + t.Error(err) + return } - return + res, _, err = test.targetConn.ReadValue() + if err != nil { + t.Error(err) + return + } + verifyResponse(res, test.expectedResponses["pattern"]) } - for i := 0; i < len(subscription.patterns); i++ { - _, _, _ = subscription.client.ReadValue() - } - } + }) - // Get fresh connection for the next phase. - conn, err := internal.GetConnection(bindAddr, port) - if err != nil { - t.Error(err) - return - } - client := resp.NewConn(conn) + t.Run("Test_HandlePublish", func(t *testing.T) { + t.Parallel() - // Check if all subscriptions are returned. - if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("CHANNELS")}); err != nil { - t.Error(err) - } - res, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - verifyExpectedResponse(res, append(channels, patterns...)) - - // Unsubscribe from one pattern and one channel before checking against a new slice of - // expected channels/patterns in the response of the "PUBSUB CHANNELS" command. - for _, unsubscribe := range []struct { - client *resp.Conn - command []resp.Value - }{ - { - client: subscribers[0], - command: []resp.Value{resp.StringValue("UNSUBSCRIBE"), resp.StringValue("channel_2"), resp.StringValue("channel_3")}, - }, - { - client: subscribers[1], - command: []resp.Value{resp.StringValue("UNSUBSCRIBE"), resp.StringValue("channel_[456]")}, - }, - } { - if err = unsubscribe.client.WriteArray(unsubscribe.command); err != nil { - t.Error(err) - } - for i := 0; i < len(unsubscribe.command[1:]); i++ { - _, _, err = unsubscribe.client.ReadValue() + var rawConnections []net.Conn + establishConnection := func() *resp.Conn { + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) } + rawConnections = append(rawConnections, conn) + return resp.NewConn(conn) } - } + defer func() { + for _, conn := range rawConnections { + _ = conn.Close() + } + }() - // Return all the remaining channels. - if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("CHANNELS")}); err != nil { - t.Error(err) - } - res, _, err = client.ReadValue() - if err != nil { - t.Error(err) - } - verifyExpectedResponse(res, []string{"channel_1", "channel_[123]"}) + // verifyChannelMessage reads the message from the connection and asserts whether + // it's the message we expect to read as a subscriber of a channel or pattern. + verifyEvent := func(client *resp.Conn, expected []string) { + rv, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + v := rv.Array() + for i := 0; i < len(v); i++ { + if v[i].String() != expected[i] { + t.Errorf("expected item at index %d to be \"%s\", got \"%s\"", i, expected[i], v[i].String()) + } + } + } - // Return only one of the remaining channels when passed a pattern that matches it. - if err = client.WriteArray([]resp.Value{ - resp.StringValue("PUBSUB"), - resp.StringValue("CHANNELS"), - resp.StringValue("channel_[189]"), - }); err != nil { - t.Error(err) - } - verifyExpectedResponse(res, []string{"channel_1"}) + // The subscribe function handles subscribing the connection to the given + // channels and patterns and reading/verifying the message sent by the server after + // subscription. + subscribe := func(client *resp.Conn, channels []string, patterns []string) { + // Subscribe to channels + command := []resp.Value{resp.StringValue("SUBSCRIBE")} + for _, channel := range channels { + command = append(command, resp.StringValue(channel)) + } + if err := client.WriteArray(command); err != nil { + t.Error(err) + } + for i := 0; i < len(channels); i++ { + // Read channel subscription confirmations. + if _, _, err := client.ReadValue(); err != nil { + t.Error(err) + } + } - // Return both remaining channels when passed a pattern that matches them. - if err := client.WriteArray([]resp.Value{ - resp.StringValue("PUBSUB"), - resp.StringValue("CHANNELS"), - resp.StringValue("channel_[123]"), - }); err != nil { - t.Error(err) - } - res, _, err = client.ReadValue() - if err != nil { - t.Error(err) - } - verifyExpectedResponse(res, []string{"channel_1", "channel_[123]"}) + // Subscribe to all the patterns + command = []resp.Value{resp.StringValue("PSUBSCRIBE")} + for _, pattern := range patterns { + command = append(command, resp.StringValue(pattern)) + } + if err := client.WriteArray(command); err != nil { + t.Error(err) + } + for i := 0; i < len(patterns); i++ { + // Read pattern subscription confirmations. + if _, _, err := client.ReadValue(); err != nil { + t.Error(err) + } + } + } - // Return no channels when passed a pattern that does not match either channel. - if err = client.WriteArray([]resp.Value{ - resp.StringValue("PUBSUB"), - resp.StringValue("CHANNELS"), - resp.StringValue("channel_[456]"), - }); err != nil { - t.Error(err) - } - res, _, err = client.ReadValue() - if err != nil { - t.Error(err) - } - verifyExpectedResponse(res, []string{}) -} + subscriptions := []struct { + client *resp.Conn + channels []string + patterns []string + }{ + { + client: establishConnection(), + channels: []string{"pub_channel_1", "pub_channel_2", "pub_channel_3"}, + patterns: []string{"pub_channel_[456]"}, + }, + { + client: establishConnection(), + channels: []string{"pub_channel_6", "pub_channel_7"}, + patterns: []string{"pub_channel_[891]"}, + }, + } + for _, subscription := range subscriptions { + // Subscribe to channels and patterns. + subscribe(subscription.client, subscription.channels, subscription.patterns) + } -func Test_HandleNumPat(t *testing.T) { - port, _ := internal.GetFreePort() - mockServer := setUpServer(bindAddr, uint16(port)) + type Subscriber struct { + client *resp.Conn + channel string + } - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - mockServer.Start() - }() - wg.Wait() + tests := []struct { + channel string + message string + subscribers []Subscriber + }{ + { + channel: "pub_channel_1", + message: "Test both subscribers 1", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_1"}, + {client: subscriptions[1].client, channel: "pub_channel_[891]"}, + }, + }, + { + channel: "pub_channel_6", + message: "Test both subscribers 2", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_[456]"}, + {client: subscriptions[1].client, channel: "pub_channel_6"}, + }, + }, + { + channel: "pub_channel_2", + message: "Test subscriber 1 1", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_2"}, + }, + }, + { + channel: "pub_channel_3", + message: "Test subscriber 1 2", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_3"}, + }, + }, + { + channel: "pub_channel_4", + message: "Test both subscribers 2", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_[456]"}, + }, + }, + { + channel: "pub_channel_5", + message: "Test subscriber 1 3", + subscribers: []Subscriber{ + {client: subscriptions[0].client, channel: "pub_channel_[456]"}, + }, + }, + { + channel: "pub_channel_7", + message: "Test subscriber 2 1", + subscribers: []Subscriber{ + {client: subscriptions[1].client, channel: "pub_channel_7"}, + }, + }, + { + channel: "pub_channel_8", + message: "Test subscriber 2 2", + subscribers: []Subscriber{ + {client: subscriptions[1].client, channel: "pub_channel_[891]"}, + }, + }, + { + channel: "pub_channel_9", + message: "Test subscriber 2 3", + subscribers: []Subscriber{ + {client: subscriptions[1].client, channel: "pub_channel_[891]"}, + }, + }, + } - // Create subscribers. - subscribers := make([]*resp.Conn, 3) - for i := 0; i < len(subscribers); i++ { - conn, err := internal.GetConnection(bindAddr, port) + // Dial echovault to make publisher connection + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return } - subscribers[i] = resp.NewConn(conn) - } + publisher := resp.NewConn(conn) - patterns := []string{"pattern_[123]", "pattern_[456]", "pattern_[789]"} - - // Subscribe to all patterns - for _, client := range subscribers { - command := []resp.Value{resp.StringValue("PSUBSCRIBE")} - for _, pattern := range patterns { - command = append(command, resp.StringValue(pattern)) - } - if err := client.WriteArray(command); err != nil { - t.Error(err) - } - // Read subscription responses to make sure we've subscribed to all the channels. - for i := 0; i < len(patterns); i++ { - res, _, err := client.ReadValue() + for _, test := range tests { + err = publisher.WriteArray([]resp.Value{ + resp.StringValue("PUBLISH"), + resp.StringValue(test.channel), + resp.StringValue(test.message), + }) if err != nil { t.Error(err) } - if len(res.Array()) != 3 { - t.Errorf("expected array response of length %d, got %d", 3, len(res.Array())) + + rv, _, err := publisher.ReadValue() + if err != nil { + t.Error(err) } - if !strings.EqualFold(res.Array()[0].String(), "psubscribe") { - t.Errorf("expected the first array item to be \"psubscribe\", got \"%s\"", res.Array()[0].String()) + if rv.String() != "OK" { + t.Errorf("Expected publish response to be \"OK\", got \"%s\"", rv.String()) } - if !slices.Contains(patterns, res.Array()[1].String()) { - t.Errorf("unexpected channel name \"%s\", expected %v", res.Array()[1].String(), patterns) + + for _, sub := range test.subscribers { + verifyEvent(sub.client, []string{"message", sub.channel, test.message}) } } - } + }) - // Get fresh connection for the next phase. - conn, err := internal.GetConnection(bindAddr, port) - if err != nil { - t.Error(err) - return - } - client := resp.NewConn(conn) + t.Run("Test_HandlePubSubChannels", func(t *testing.T) { + t.Parallel() - // Check that we receive all the patterns with NUMPAT commands. - if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { - t.Error(err) - } - res, _, err := client.ReadValue() - if res.Integer() != len(patterns) { - t.Errorf("expected response \"%d\", got \"%d\"", len(patterns), res.Integer()) - } + verifyExpectedResponse := func(res resp.Value, expected []string) { + if len(res.Array()) != len(expected) { + t.Errorf("expected response array of length %d, got %d", len(expected), len(res.Array())) + } + for _, e := range expected { + if !slices.ContainsFunc(res.Array(), func(v resp.Value) bool { + return e == v.String() + }) { + t.Errorf("expected to find element \"%s\" in response array, could not find it", e) + } + } + } - // Unsubscribe all subscribers from one pattern and check if the response is updated. - for _, subscriber := range subscribers { - if err = subscriber.WriteArray([]resp.Value{ - resp.StringValue("PUNSUBSCRIBE"), - resp.StringValue(patterns[0]), + port, err := internal.GetFreePort() + if err != nil { + t.Error(err) + return + } + mockServer, err := setUpServer(port) + if err != nil { + t.Error(err) + return + } + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + wg.Done() + mockServer.Start() + }() + wg.Wait() + + subscribers := make([]*resp.Conn, 2) + for i := 0; i < len(subscribers); i++ { + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + return + } + subscribers[i] = resp.NewConn(conn) + } + + channels := []string{"channel_1", "channel_2", "channel_3"} + patterns := []string{"channel_[123]", "channel_[456]"} + + subscriptions := []struct { + client *resp.Conn + action string + channels []string + patterns []string + }{ + { + client: subscribers[0], + action: "SUBSCRIBE", + channels: channels, + patterns: make([]string, 0), + }, + { + client: subscribers[1], + action: "PSUBSCRIBE", + channels: make([]string, 0), + patterns: patterns, + }, + } + for _, subscription := range subscriptions { + command := []resp.Value{resp.StringValue(subscription.action)} + if len(subscription.channels) > 0 { + for _, channel := range subscription.channels { + command = append(command, resp.StringValue(channel)) + } + } else if len(subscription.patterns) > 0 { + for _, pattern := range subscription.patterns { + command = append(command, resp.StringValue(pattern)) + } + } + if err := subscription.client.WriteArray(command); err != nil { + t.Error(err) + } + if len(subscription.channels) > 0 { + for i := 0; i < len(subscription.channels); i++ { + _, _, _ = subscription.client.ReadValue() + } + return + } + for i := 0; i < len(subscription.patterns); i++ { + _, _, _ = subscription.client.ReadValue() + } + } + + // Get fresh connection for the next phase. + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + return + } + client := resp.NewConn(conn) + + // Check if all subscriptions are returned. + if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("CHANNELS")}); err != nil { + t.Error(err) + } + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + verifyExpectedResponse(res, append(channels, patterns...)) + + // Unsubscribe from one pattern and one channel before checking against a new slice of + // expected channels/patterns in the response of the "PUBSUB CHANNELS" command. + for _, unsubscribe := range []struct { + client *resp.Conn + command []resp.Value + }{ + { + client: subscribers[0], + command: []resp.Value{resp.StringValue("UNSUBSCRIBE"), resp.StringValue("channel_2"), resp.StringValue("channel_3")}, + }, + { + client: subscribers[1], + command: []resp.Value{resp.StringValue("UNSUBSCRIBE"), resp.StringValue("channel_[456]")}, + }, + } { + if err = unsubscribe.client.WriteArray(unsubscribe.command); err != nil { + t.Error(err) + } + for i := 0; i < len(unsubscribe.command[1:]); i++ { + _, _, err = unsubscribe.client.ReadValue() + if err != nil { + t.Error(err) + } + } + } + + // Return all the remaining channels. + if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("CHANNELS")}); err != nil { + t.Error(err) + } + res, _, err = client.ReadValue() + if err != nil { + t.Error(err) + } + verifyExpectedResponse(res, []string{"channel_1", "channel_[123]"}) + + // Return only one of the remaining channels when passed a pattern that matches it. + if err = client.WriteArray([]resp.Value{ + resp.StringValue("PUBSUB"), + resp.StringValue("CHANNELS"), + resp.StringValue("channel_[189]"), }); err != nil { t.Error(err) } - res, _, err = subscriber.ReadValue() + verifyExpectedResponse(res, []string{"channel_1"}) + + // Return both remaining channels when passed a pattern that matches them. + if err := client.WriteArray([]resp.Value{ + resp.StringValue("PUBSUB"), + resp.StringValue("CHANNELS"), + resp.StringValue("channel_[123]"), + }); err != nil { + t.Error(err) + } + res, _, err = client.ReadValue() if err != nil { t.Error(err) } - if len(res.Array()[0].Array()) != 3 { - t.Errorf("expected array response of length %d, got %d", 3, len(res.Array()[0].Array())) - } - if !strings.EqualFold(res.Array()[0].Array()[0].String(), "punsubscribe") { - t.Errorf("expected the first array item to be \"punsubscribe\", got \"%s\"", res.Array()[0].Array()[0].String()) - } - if res.Array()[0].Array()[1].String() != patterns[0] { - t.Errorf("unexpected channel name \"%s\", expected %s", res.Array()[0].Array()[1].String(), patterns[0]) - } - } - if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { - t.Error(err) - } - res, _, err = client.ReadValue() - if res.Integer() != len(patterns)-1 { - t.Errorf("expected response \"%d\", got \"%d\"", len(patterns)-1, res.Integer()) - } + verifyExpectedResponse(res, []string{"channel_1", "channel_[123]"}) - // Unsubscribe from all the channels and check if we get a 0 response - for _, subscriber := range subscribers { - for _, pattern := range patterns[1:] { + // Return no channels when passed a pattern that does not match either channel. + if err = client.WriteArray([]resp.Value{ + resp.StringValue("PUBSUB"), + resp.StringValue("CHANNELS"), + resp.StringValue("channel_[456]"), + }); err != nil { + t.Error(err) + } + res, _, err = client.ReadValue() + if err != nil { + t.Error(err) + } + verifyExpectedResponse(res, []string{}) + }) + + t.Run("Test_HandleNumPat", func(t *testing.T) { + t.Parallel() + + port, err := internal.GetFreePort() + if err != nil { + t.Error(err) + return + } + + mockServer, err := setUpServer(port) + if err != nil { + t.Error(err) + return + } + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + wg.Done() + mockServer.Start() + }() + wg.Wait() + + // Create subscribers. + subscribers := make([]*resp.Conn, 3) + for i := 0; i < len(subscribers); i++ { + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + return + } + subscribers[i] = resp.NewConn(conn) + } + + patterns := []string{"pattern_[123]", "pattern_[456]", "pattern_[789]"} + + // Subscribe to all patterns + for _, client := range subscribers { + command := []resp.Value{resp.StringValue("PSUBSCRIBE")} + for _, pattern := range patterns { + command = append(command, resp.StringValue(pattern)) + } + if err := client.WriteArray(command); err != nil { + t.Error(err) + } + // Read subscription responses to make sure we've subscribed to all the channels. + for i := 0; i < len(patterns); i++ { + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + if len(res.Array()) != 3 { + t.Errorf("expected array response of length %d, got %d", 3, len(res.Array())) + } + if !strings.EqualFold(res.Array()[0].String(), "psubscribe") { + t.Errorf("expected the first array item to be \"psubscribe\", got \"%s\"", res.Array()[0].String()) + } + if !slices.Contains(patterns, res.Array()[1].String()) { + t.Errorf("unexpected channel name \"%s\", expected %v", res.Array()[1].String(), patterns) + } + } + } + + // Get fresh connection for the next phase. + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + return + } + client := resp.NewConn(conn) + + // Check that we receive all the patterns with NUMPAT commands. + if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { + t.Error(err) + } + res, _, err := client.ReadValue() + if res.Integer() != len(patterns) { + t.Errorf("expected response \"%d\", got \"%d\"", len(patterns), res.Integer()) + } + + // Unsubscribe all subscribers from one pattern and check if the response is updated. + for _, subscriber := range subscribers { if err = subscriber.WriteArray([]resp.Value{ resp.StringValue("PUNSUBSCRIBE"), - resp.StringValue(pattern), + resp.StringValue(patterns[0]), }); err != nil { t.Error(err) } @@ -807,147 +824,190 @@ func Test_HandleNumPat(t *testing.T) { t.Error(err) } if len(res.Array()[0].Array()) != 3 { - t.Errorf("expected array response of length %d, got %d", 3, - len(res.Array()[0].Array())) + t.Errorf("expected array response of length %d, got %d", 3, len(res.Array()[0].Array())) } if !strings.EqualFold(res.Array()[0].Array()[0].String(), "punsubscribe") { - t.Errorf("expected the first array item to be \"punsubscribe\", got \"%s\"", - res.Array()[0].Array()[0].String()) + t.Errorf("expected the first array item to be \"punsubscribe\", got \"%s\"", res.Array()[0].Array()[0].String()) } - if res.Array()[0].Array()[1].String() != pattern { - t.Errorf("unexpected channel name \"%s\", expected %s", - res.Array()[0].Array()[1].String(), pattern) + if res.Array()[0].Array()[1].String() != patterns[0] { + t.Errorf("unexpected channel name \"%s\", expected %s", res.Array()[0].Array()[1].String(), patterns[0]) } } - } - if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { - t.Error(err) - } - res, _, err = client.ReadValue() - if res.Integer() != 0 { - t.Errorf("expected response \"%d\", got \"%d\"", 0, res.Integer()) - } -} + if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { + t.Error(err) + } + res, _, err = client.ReadValue() + if res.Integer() != len(patterns)-1 { + t.Errorf("expected response \"%d\", got \"%d\"", len(patterns)-1, res.Integer()) + } -func Test_HandleNumSub(t *testing.T) { - port, _ := internal.GetFreePort() - mockServer := setUpServer(bindAddr, uint16(port)) + // Unsubscribe from all the channels and check if we get a 0 response + for _, subscriber := range subscribers { + for _, pattern := range patterns[1:] { + if err = subscriber.WriteArray([]resp.Value{ + resp.StringValue("PUNSUBSCRIBE"), + resp.StringValue(pattern), + }); err != nil { + t.Error(err) + } + res, _, err = subscriber.ReadValue() + if err != nil { + t.Error(err) + } + if len(res.Array()[0].Array()) != 3 { + t.Errorf("expected array response of length %d, got %d", 3, + len(res.Array()[0].Array())) + } + if !strings.EqualFold(res.Array()[0].Array()[0].String(), "punsubscribe") { + t.Errorf("expected the first array item to be \"punsubscribe\", got \"%s\"", + res.Array()[0].Array()[0].String()) + } + if res.Array()[0].Array()[1].String() != pattern { + t.Errorf("unexpected channel name \"%s\", expected %s", + res.Array()[0].Array()[1].String(), pattern) + } + } + } + if err = client.WriteArray([]resp.Value{resp.StringValue("PUBSUB"), resp.StringValue("NUMPAT")}); err != nil { + t.Error(err) + } + res, _, err = client.ReadValue() + if res.Integer() != 0 { + t.Errorf("expected response \"%d\", got \"%d\"", 0, res.Integer()) + } + }) - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - wg.Done() - mockServer.Start() - }() - wg.Wait() + t.Run("Test_HandleNumSub", func(t *testing.T) { + t.Parallel() - channels := []string{"channel_1", "channel_2", "channel_3"} + port, err := internal.GetFreePort() + if err != nil { + t.Error(err) + return + } - for i := 0; i < 3; i++ { - conn, err := internal.GetConnection(bindAddr, port) + mockServer, err := setUpServer(port) + if err != nil { + t.Error(err) + return + } + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + wg.Done() + mockServer.Start() + }() + wg.Wait() + + channels := []string{"channel_1", "channel_2", "channel_3"} + + for i := 0; i < 3; i++ { + conn, err := internal.GetConnection("localhost", port) + if err != nil { + t.Error(err) + return + } + client := resp.NewConn(conn) + command := []resp.Value{ + resp.StringValue("SUBSCRIBE"), + } + for _, channel := range channels { + command = append(command, resp.StringValue(channel)) + } + err = client.WriteArray(command) + if err != nil { + t.Error(err) + } + + // Read subscription responses to make sure we've subscribed to all the channels. + for i := 0; i < len(channels); i++ { + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + if len(res.Array()) != 3 { + t.Errorf("expected array response of length %d, got %d", 3, len(res.Array())) + } + if !strings.EqualFold(res.Array()[0].String(), "subscribe") { + t.Errorf("expected the first array item to be \"subscribe\", got \"%s\"", res.Array()[0].String()) + } + if !slices.Contains(channels, res.Array()[1].String()) { + t.Errorf("unexpected channel name \"%s\", expected %v", res.Array()[1].String(), channels) + } + } + } + + // Get fresh connection for the next phase. + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return } client := resp.NewConn(conn) - command := []resp.Value{ - resp.StringValue("SUBSCRIBE"), - } - for _, channel := range channels { - command = append(command, resp.StringValue(channel)) - } - err = client.WriteArray(command) - if err != nil { - t.Error(err) - } - // Read subscription responses to make sure we've subscribed to all the channels. - for i := 0; i < len(channels); i++ { - res, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - if len(res.Array()) != 3 { - t.Errorf("expected array response of length %d, got %d", 3, len(res.Array())) - } - if !strings.EqualFold(res.Array()[0].String(), "subscribe") { - t.Errorf("expected the first array item to be \"subscribe\", got \"%s\"", res.Array()[0].String()) - } - if !slices.Contains(channels, res.Array()[1].String()) { - t.Errorf("unexpected channel name \"%s\", expected %v", res.Array()[1].String(), channels) - } - } - } - - // Get fresh connection for the next phase. - conn, err := internal.GetConnection(bindAddr, port) - if err != nil { - t.Error(err) - return - } - client := resp.NewConn(conn) - - tests := []struct { - name string - cmd []string - expectedResponse [][]string - }{ - { - name: "1. Get all subscriptions on existing channels", - cmd: append([]string{"PUBSUB", "NUMSUB"}, channels...), - expectedResponse: [][]string{{"channel_1", "3"}, {"channel_2", "3"}, {"channel_3", "3"}}, - }, - { - name: "2. Get all the subscriptions of on existing channels and a few non-existent ones", - cmd: append([]string{"PUBSUB", "NUMSUB", "non_existent_channel_1", "non_existent_channel_2"}, channels...), - expectedResponse: [][]string{ - {"non_existent_channel_1", "0"}, - {"non_existent_channel_2", "0"}, - {"channel_1", "3"}, - {"channel_2", "3"}, - {"channel_3", "3"}, + tests := []struct { + name string + cmd []string + expectedResponse [][]string + }{ + { + name: "1. Get all subscriptions on existing channels", + cmd: append([]string{"PUBSUB", "NUMSUB"}, channels...), + expectedResponse: [][]string{{"channel_1", "3"}, {"channel_2", "3"}, {"channel_3", "3"}}, }, - }, - { - name: "3. Get an empty array when channels are not provided in the command", - cmd: []string{"PUBSUB", "NUMSUB"}, - expectedResponse: make([][]string, 0), - }, - } + { + name: "2. Get all the subscriptions of on existing channels and a few non-existent ones", + cmd: append([]string{"PUBSUB", "NUMSUB", "non_existent_channel_1", "non_existent_channel_2"}, channels...), + expectedResponse: [][]string{ + {"non_existent_channel_1", "0"}, + {"non_existent_channel_2", "0"}, + {"channel_1", "3"}, + {"channel_2", "3"}, + {"channel_3", "3"}, + }, + }, + { + name: "3. Get an empty array when channels are not provided in the command", + cmd: []string{"PUBSUB", "NUMSUB"}, + expectedResponse: make([][]string, 0), + }, + } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var command []resp.Value - for _, token := range test.cmd { - command = append(command, resp.StringValue(token)) - } - - if err = client.WriteArray(command); err != nil { - t.Error(err) - } - - res, _, err := client.ReadValue() - if err != nil { - t.Error(err) - } - - arr := res.Array() - if len(arr) != len(test.expectedResponse) { - t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(arr)) - } - - for _, item := range arr { - itemArr := item.Array() - if len(itemArr) != 2 { - t.Errorf("expected each response item to be of length 2, got %d", len(itemArr)) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var command []resp.Value + for _, token := range test.cmd { + command = append(command, resp.StringValue(token)) } - if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool { - return expected[0] == itemArr[0].String() && expected[1] == itemArr[1].String() - }) { - t.Errorf("could not find entry with channel \"%s\", with %d subscribers in expected response", - itemArr[0].String(), itemArr[1].Integer()) + + if err = client.WriteArray(command); err != nil { + t.Error(err) } - } - }) - } + + res, _, err := client.ReadValue() + if err != nil { + t.Error(err) + } + + arr := res.Array() + if len(arr) != len(test.expectedResponse) { + t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(arr)) + } + + for _, item := range arr { + itemArr := item.Array() + if len(itemArr) != 2 { + t.Errorf("expected each response item to be of length 2, got %d", len(itemArr)) + } + if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool { + return expected[0] == itemArr[0].String() && expected[1] == itemArr[1].String() + }) { + t.Errorf("could not find entry with channel \"%s\", with %d subscribers in expected response", + itemArr[0].String(), itemArr[1].Integer()) + } + } + }) + } + }) } diff --git a/internal/modules/set/commands_test.go b/internal/modules/set/commands_test.go index 2971c31..dd9af2c 100644 --- a/internal/modules/set/commands_test.go +++ b/internal/modules/set/commands_test.go @@ -16,18 +16,15 @@ package set_test import ( "errors" - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/echovault/echovault/internal/modules/set" "github.com/tidwall/resp" - "net" "slices" "strconv" "strings" - "sync" "testing" ) @@ -51,13 +48,9 @@ func Test_Set(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -65,7 +58,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSADD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -213,7 +206,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSCARD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -340,7 +333,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSDIFF", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -490,7 +483,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSDIFFSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -672,7 +665,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSINTER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -822,7 +815,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSINTERCARD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -977,7 +970,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSINTERSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1159,7 +1152,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSISMEMBER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1281,7 +1274,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSMEMBERS", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1408,7 +1401,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSMISMEMBER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1535,7 +1528,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSMOVE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1731,7 +1724,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSPOP", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1873,7 +1866,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSRANDMEMBER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2038,7 +2031,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSREM", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2179,7 +2172,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSUNION", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2322,7 +2315,7 @@ func Test_Set(t *testing.T) { t.Run("Test_HandleSUNIONSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return diff --git a/internal/modules/sorted_set/commands_test.go b/internal/modules/sorted_set/commands_test.go index f7e6a73..1d6dd5d 100644 --- a/internal/modules/sorted_set/commands_test.go +++ b/internal/modules/sorted_set/commands_test.go @@ -16,7 +16,6 @@ package sorted_set_test import ( "errors" - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" @@ -24,11 +23,9 @@ import ( "github.com/echovault/echovault/internal/modules/sorted_set" "github.com/tidwall/resp" "math" - "net" "slices" "strconv" "strings" - "sync" "testing" ) @@ -52,13 +49,9 @@ func Test_SortedSet(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -66,7 +59,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZADD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -282,7 +275,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZCARD", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -413,7 +406,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZCOUNT", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -588,7 +581,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZLEXCOUNT", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -739,7 +732,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZDIFF", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -959,7 +952,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZDIFFSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1202,7 +1195,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZINCRBY", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1485,7 +1478,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZMPOP", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -1800,7 +1793,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZPOP", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2061,7 +2054,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZMSCORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2189,7 +2182,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZSCORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2326,7 +2319,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZRANDMEMBER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2523,7 +2516,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZRANK", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2680,7 +2673,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZREM", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -2853,7 +2846,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZREMRANGEBYSCORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -3029,7 +3022,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZREMRANGEBYRANK", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -3259,7 +3252,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZREMRANGEBYLEX", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -3460,7 +3453,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZRANGE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -3761,7 +3754,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZRANGESTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -4112,7 +4105,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZINTER", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -4480,7 +4473,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZINTERSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -4895,7 +4888,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZUNION", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return @@ -5288,7 +5281,7 @@ func Test_SortedSet(t *testing.T) { t.Run("Test_HandleZUNIONSTORE", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error() return diff --git a/internal/modules/string/commands_test.go b/internal/modules/string/commands_test.go index c67e182..c92e419 100644 --- a/internal/modules/string/commands_test.go +++ b/internal/modules/string/commands_test.go @@ -16,16 +16,13 @@ package str_test import ( "errors" - "fmt" "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/tidwall/resp" - "net" "strconv" "strings" - "sync" "testing" ) @@ -49,13 +46,9 @@ func Test_String(t *testing.T) { return } - wg := sync.WaitGroup{} - wg.Add(1) go func() { - wg.Done() mockServer.Start() }() - wg.Wait() t.Cleanup(func() { mockServer.ShutDown() @@ -63,7 +56,7 @@ func Test_String(t *testing.T) { t.Run("Test_HandleSetRange", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -213,7 +206,7 @@ func Test_String(t *testing.T) { t.Run("Test_HandleStrLen", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return @@ -314,7 +307,7 @@ func Test_String(t *testing.T) { t.Run("Test_HandleSubStr", func(t *testing.T) { t.Parallel() - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) + conn, err := internal.GetConnection("localhost", port) if err != nil { t.Error(err) return diff --git a/internal/utils.go b/internal/utils.go index 47e04a7..fe5a8c0 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -18,6 +18,7 @@ import ( "bufio" "bytes" "cmp" + "crypto/tls" "errors" "fmt" "github.com/echovault/echovault/internal/constants" @@ -30,6 +31,7 @@ import ( "slices" "strconv" "strings" + "syscall" "time" "github.com/sethvargo/go-retry" @@ -429,15 +431,51 @@ func GetFreePort() (int, error) { } func GetConnection(addr string, port int) (net.Conn, error) { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port)) - if err != nil { - return nil, err - } - for { - // Wait until connection is no longer nil. - if conn != nil { + var conn net.Conn + var err error + done := make(chan struct{}) + + go func() { + for { + conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port)) + if err != nil && errors.Is(err.(*net.OpError), syscall.ECONNREFUSED) { + // If we get a "connection refused error, try again." + continue + } break } + done <- struct{}{} + }() + + select { + case <-time.After(10 * time.Second): + return nil, errors.New("connection timeout") + case <-done: + return conn, err + } +} + +func GetTLSConnection(addr string, port int, config *tls.Config) (net.Conn, error) { + var conn net.Conn + var err error + done := make(chan struct{}) + + go func() { + for { + conn, err = tls.Dial("tcp", fmt.Sprintf("%s:%d", addr, port), config) + if err != nil && errors.Is(err.(*net.OpError), syscall.ECONNREFUSED) { + // If we get a "connection refused error, try again." + continue + } + break + } + done <- struct{}{} + }() + + select { + case <-time.After(10 * time.Second): + return nil, errors.New("connection timeout") + case <-done: + return conn, err } - return conn, nil }