diff --git a/coverage/coverage.out b/coverage/coverage.out index 7e55890..99b9024 100644 --- a/coverage/coverage.out +++ b/coverage/coverage.out @@ -1005,12 +1005,6 @@ github.com/echovault/echovault/echovault/test_helpers.go:22.2,22.57 1 1 github.com/echovault/echovault/echovault/test_helpers.go:22.57,24.3 1 0 github.com/echovault/echovault/echovault/test_helpers.go:25.2,26.12 2 1 github.com/echovault/echovault/echovault/test_helpers.go:29.95,34.2 4 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:35.36,43.84 1 1 -github.com/echovault/echovault/internal/modules/connection/commands.go:43.84,49.5 1 0 github.com/echovault/echovault/internal/modules/acl/acl.go:52.40,57.24 3 1 github.com/echovault/echovault/internal/modules/acl/acl.go:57.24,65.3 2 1 github.com/echovault/echovault/internal/modules/acl/acl.go:68.2,68.28 1 1 @@ -1448,99 +1442,6 @@ github.com/echovault/echovault/internal/modules/acl/user.go:289.40,304.2 1 1 github.com/echovault/echovault/internal/modules/acl/user.go:306.46,307.24 1 1 github.com/echovault/echovault/internal/modules/acl/user.go:307.24,309.3 1 1 github.com/echovault/echovault/internal/modules/acl/user.go:310.2,310.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 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:76.35,77.65 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:77.65,78.41 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:78.41,80.5 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:81.4,81.12 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:83.3,83.13 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:86.2,86.51 1 0 -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 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:213.84,217.5 1 0 -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 0 -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 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:335.86,339.7 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:340.75,341.34 1 0 -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 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:345.34,347.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:348.7,348.75 1 0 -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 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:360.86,364.7 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:365.75,366.35 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:366.35,368.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:369.7,370.47 2 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:379.86,383.7 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:384.75,387.38 3 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:387.38,389.8 1 0 -github.com/echovault/echovault/internal/modules/admin/commands.go:390.7,390.30 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 1 github.com/echovault/echovault/internal/modules/generic/commands.go:39.2,45.16 6 1 @@ -1913,9 +1814,9 @@ github.com/echovault/echovault/internal/modules/hash/commands.go:323.38,325.17 2 github.com/echovault/echovault/internal/modules/hash/commands.go:325.17,326.41 1 1 github.com/echovault/echovault/internal/modules/hash/commands.go:326.41,328.13 2 1 github.com/echovault/echovault/internal/modules/hash/commands.go:330.4,330.42 1 1 -github.com/echovault/echovault/internal/modules/hash/commands.go:330.42,333.13 3 0 -github.com/echovault/echovault/internal/modules/hash/commands.go:335.4,335.38 1 1 -github.com/echovault/echovault/internal/modules/hash/commands.go:335.38,337.13 2 1 +github.com/echovault/echovault/internal/modules/hash/commands.go:330.42,333.13 3 1 +github.com/echovault/echovault/internal/modules/hash/commands.go:335.4,335.38 1 0 +github.com/echovault/echovault/internal/modules/hash/commands.go:335.38,337.13 2 0 github.com/echovault/echovault/internal/modules/hash/commands.go:342.2,342.25 1 1 github.com/echovault/echovault/internal/modules/hash/commands.go:345.68,347.16 2 1 github.com/echovault/echovault/internal/modules/hash/commands.go:347.16,349.3 1 1 @@ -2055,6 +1956,105 @@ 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/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 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:76.35,77.65 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:77.65,78.41 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:78.41,80.5 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:81.4,81.12 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:83.3,83.13 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:86.2,86.51 1 0 +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 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:213.84,217.5 1 0 +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 0 +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 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:335.86,339.7 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:340.75,341.34 1 0 +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 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:345.34,347.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:348.7,348.75 1 0 +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 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:360.86,364.7 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:365.75,366.35 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:366.35,368.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:369.7,370.47 2 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:379.86,383.7 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:384.75,387.38 3 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:387.38,389.8 1 0 +github.com/echovault/echovault/internal/modules/admin/commands.go:390.7,390.30 1 0 +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:35.36,43.84 1 1 +github.com/echovault/echovault/internal/modules/connection/commands.go:43.84,49.5 1 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 1 github.com/echovault/echovault/internal/modules/list/commands.go:33.2,35.44 2 1 diff --git a/internal/aof/log/store.go b/internal/aof/log/store.go index 18f2e70..1099311 100644 --- a/internal/aof/log/store.go +++ b/internal/aof/log/store.go @@ -15,11 +15,9 @@ package log import ( - "bufio" - "bytes" - "errors" "fmt" "github.com/echovault/echovault/internal/clock" + "github.com/tidwall/resp" "io" "log" "os" @@ -38,7 +36,7 @@ type AppendReadWriter interface { type AppendStore struct { clock clock.Clock - strategy string // Append file sync strategy. Can only be "always", "everysec", or "no + strategy string // Append file sync strategy. Can only be "always", "everysec", or "no" mut sync.Mutex // Store mutex rw AppendReadWriter // The ReadWriter used to persist and load the log directory string // The directory for the AOF file if we must create one @@ -53,7 +51,7 @@ func WithClock(clock clock.Clock) func(store *AppendStore) { func WithStrategy(strategy string) func(store *AppendStore) { return func(store *AppendStore) { - store.strategy = strategy + store.strategy = strings.ToLower(strategy) } } @@ -75,7 +73,7 @@ func WithHandleCommandFunc(f func(command []byte)) func(store *AppendStore) { } } -func NewAppendStore(options ...func(store *AppendStore)) *AppendStore { +func NewAppendStore(options ...func(store *AppendStore)) (*AppendStore, error) { store := &AppendStore{ clock: clock.NewClock(), directory: "", @@ -94,11 +92,11 @@ func NewAppendStore(options ...func(store *AppendStore)) *AppendStore { // Create the directory if it does not exist err := os.MkdirAll(path.Join(store.directory, "aof"), os.ModePerm) if err != nil { - log.Println(fmt.Errorf("new append store -> mkdir error: %+v", err)) + return nil, fmt.Errorf("new append store -> mkdir error: %+v", err) } f, err := os.OpenFile(path.Join(store.directory, "aof", "log.aof"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) if err != nil { - log.Println(fmt.Errorf("new append store -> open file error: %+v", err)) + return nil, fmt.Errorf("new append store -> open file error: %+v", err) } store.rw = f } @@ -108,40 +106,46 @@ func NewAppendStore(options ...func(store *AppendStore)) *AppendStore { if strings.EqualFold(store.strategy, "everysec") { go func() { for { + store.mut.Lock() if err := store.Sync(); err != nil { + store.mut.Unlock() log.Println(fmt.Errorf("new append store error: %+v", err)) break } + store.mut.Unlock() <-store.clock.After(1 * time.Second) } }() } - return store + + return store, nil } func (store *AppendStore) Write(command []byte) error { - store.mut.Lock() - defer store.mut.Unlock() // Skip operation if ReadWriter is not defined if store.rw == nil { return nil } + + store.mut.Lock() + defer store.mut.Unlock() + // Add new line before writing to AOF file. out := append(command, []byte("\r\n")...) if _, err := store.rw.Write(out); err != nil { return err } + if strings.EqualFold(store.strategy, "always") { if err := store.Sync(); err != nil { return err } } + return nil } func (store *AppendStore) Sync() error { - store.mut.Lock() - defer store.mut.Unlock() if store.rw != nil { return store.rw.Sync() } @@ -152,33 +156,26 @@ func (store *AppendStore) Restore() error { store.mut.Lock() defer store.mut.Unlock() - buf := bufio.NewReader(store.rw) - - var commands [][]byte - var line []byte - - for { - b, _, err := buf.ReadLine() - if err != nil && errors.Is(err, io.EOF) { - break - } else if err != nil { - return err - } - if len(b) <= 0 { - line = append(line, []byte("\r\n\r\n")...) - commands = append(commands, line) - line = []byte{} - continue - } - if len(line) > 0 { - line = append(line, append([]byte("\r\n"), bytes.TrimLeft(b, "\x00")...)...) - continue - } - line = append(line, bytes.TrimLeft(b, "\x00")...) + // Move cursor to the beginning of the file + if _, err := store.rw.Seek(0, 0); err != nil { + return fmt.Errorf("restore aof: %v", err) } - for _, c := range commands { - store.handleCommand(c) + r := resp.NewReader(store.rw) + for { + value, n, err := r.ReadValue() + if err != nil && err != io.EOF { + return err + } + if n == 0 { + // Break out when there are no more bytes to read + break + } + command, err := value.MarshalRESP() + if err != nil { + return err + } + store.handleCommand(command) } return nil diff --git a/internal/aof/log/store_test.go b/internal/aof/log/store_test.go new file mode 100644 index 0000000..e5a3b56 --- /dev/null +++ b/internal/aof/log/store_test.go @@ -0,0 +1,144 @@ +// Copyright 2024 Kelvin Clement Mwinuka +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log_test + +import ( + "bytes" + "fmt" + "github.com/echovault/echovault/internal/aof/log" + "github.com/echovault/echovault/internal/clock" + "os" + "path" + "testing" + "time" +) + +func marshalRespCommand(command []string) []byte { + return []byte(fmt.Sprintf( + "*%d\r\n$%d\r\n%s\r\n$%d\r\n%s\r\n$%d\r\n%s", len(command), + len(command[0]), command[0], + len(command[1]), command[1], + len(command[2]), command[2], + )) +} + +func Test_LogWithNoReadWriter(t *testing.T) { + tests := []struct { + name string + directory string + strategy string + commands [][]string + appendReadWriter log.AppendReadWriter + }{ + { + name: "1. Not passing an AppendReadWriter to NewAppendStore should create a new append file", + directory: "./testdata/with_no_read_writer", + strategy: "always", + commands: [][]string{ + {"SET", "key1", "value1"}, + {"SET", "key2", "value2"}, + {"SET", "key3", "value3"}, + }, + appendReadWriter: nil, + }, + { + name: "2. Passing an existing AppendReadWriter to NewAppendStore should successfully append and restore", + directory: "./testdata/with_read_writer", + strategy: "always", + commands: [][]string{ + {"SET", "key1", "value1"}, + {"SET", "key2", "value2"}, + {"SET", "key3", "value3"}, + }, + appendReadWriter: func() log.AppendReadWriter { + // Create the directory if it does not exist + if err := os.MkdirAll(path.Join("./testdata/with_read_writer", "aof"), os.ModePerm); err != nil { + t.Error(err) + } + f, err := os.OpenFile(path.Join("./testdata/with_read_writer", "aof", "log.aof"), + os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + if err != nil { + t.Error(err) + } + return f + }(), + }, + { + name: "3. Using everysec strategy should sync the AOF file after one second", + directory: "./testdata/with_everysec_strategy", + strategy: "everysec", + commands: [][]string{ + {"SET", "key1", "value1"}, + {"SET", "key2", "value2"}, + {"SET", "key3", "value3"}, + }, + appendReadWriter: nil, + }, + } + + for _, test := range tests { + done := make(chan struct{}, 1) + + options := []func(store *log.AppendStore){ + log.WithClock(clock.NewClock()), + log.WithDirectory(test.directory), + log.WithStrategy(test.strategy), + log.WithHandleCommandFunc(func(command []byte) { + for _, c := range test.commands { + if bytes.Contains(command, marshalRespCommand(c)) { + return + } + } + t.Errorf("could not find command in commands list:\n%s", string(command)) + }), + } + if test.appendReadWriter != nil { + options = append(options, log.WithReadWriter(test.appendReadWriter)) + } + + go func() { + store, err := log.NewAppendStore(options...) + if err != nil { + t.Error(err) + } + + for _, command := range test.commands { + b := marshalRespCommand(command) + if err = store.Write(b); err != nil { + t.Error(err) + } + } + + // Restore from AOF file + if err = store.Restore(); err != nil { + t.Error(err) + } + + if err = store.Close(); err != nil { + t.Error(err) + } + + done <- struct{}{} + }() + + select { + case <-done: + _ = os.RemoveAll(test.directory) + case <-time.After(200 * time.Millisecond): + _ = os.RemoveAll(test.directory) + t.Error("timeout error") + } + } +} diff --git a/internal/aof/preamble/store.go b/internal/aof/preamble/store.go index 4d51dd2..7c97359 100644 --- a/internal/aof/preamble/store.go +++ b/internal/aof/preamble/store.go @@ -20,7 +20,6 @@ import ( "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/clock" "io" - "log" "os" "path" "sync" @@ -72,7 +71,7 @@ func WithDirectory(directory string) func(store *PreambleStore) { } } -func NewPreambleStore(options ...func(store *PreambleStore)) *PreambleStore { +func NewPreambleStore(options ...func(store *PreambleStore)) (*PreambleStore, error) { store := &PreambleStore{ clock: clock.NewClock(), rw: nil, @@ -93,16 +92,16 @@ func NewPreambleStore(options ...func(store *PreambleStore)) *PreambleStore { if store.rw == nil && store.directory != "" { err := os.MkdirAll(path.Join(store.directory, "aof"), os.ModePerm) if err != nil { - log.Println(fmt.Errorf("new preamble store -> mkdir error: %+v", err)) + return nil, fmt.Errorf("new preamble store -> mkdir error: %+v", err) } f, err := os.OpenFile(path.Join(store.directory, "aof", "preamble.bin"), os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { - log.Println(fmt.Errorf("new preamble store -> open file error: %+v", err)) + return nil, fmt.Errorf("new preamble store -> open file error: %+v", err) } store.rw = f } - return store + return store, nil } func (store *PreambleStore) CreatePreamble() error {