-
Notifications
You must be signed in to change notification settings - Fork 0
Load & integration testing for Raft #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/raft-in-progress
Are you sure you want to change the base?
Changes from 1 commit
4aa8fee
23b0ad4
b90ae4f
ac6b302
a5ab932
5ca69a2
053e60e
e30c4c5
140c65a
b3127f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,7 +1,15 @@ | ||||||
--- | ||||||
version: '3.8' | ||||||
services: | ||||||
app: | ||||||
tkvpp-node-standalone: | ||||||
image: tinykvpp-clang:latest | ||||||
container_name: tkvpp-node-1 | ||||||
security_opt: | ||||||
- seccomp:unconfined | ||||||
container_name: tkvpp-node-standalone | ||||||
command: ["/app/tkvpp", "-c", "/app/config.json"] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t repeat the binary when image has ENTRYPOINT With ENTRYPOINT ["/app/tkvpp"] in the image, your current command repeats the binary, leading to "/app/tkvpp /app/tkvpp -c ...". Pass only flags. Apply: - command: ["/app/tkvpp", "-c", "/app/config.json"]
+ command: ["-c", "/app/config.json"] 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||
ports: | ||||||
- "9891:9891" | ||||||
volumes: | ||||||
- ./assets/tkvpp_config_standalone.json:/app/config.json | ||||||
- tkvpp-node-standalone:/var/tkvpp | ||||||
volumes: | ||||||
tkvpp-node-standalone: |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,5 @@ | ||||||||||||||
package main | ||||||||||||||
|
||||||||||||||
import () | ||||||||||||||
|
import () | |
package main | |
import "github.com/lnikon/tinykvpp/tests/go/kvtest/cmd" | |
func main() { cmd.Execute() } |
🤖 Prompt for AI Agents
In tests/go/kvtest/cmd/kvtest/main.go around line 3 there is an empty import
block (`import ()`) which causes a Go compile error; either delete this file
entirely (preferred to avoid duplicate entrypoints) or replace the empty import
with the actual imports and wire the file into Cobra by adding the needed
imports and invoking the root command (e.g., import the cobra/root command
package and call its Execute in main) so the file becomes a valid entrypoint.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Avoid duplicate entrypoints; keep only one main.
You now have two mains:
- tests/go/kvtest/main.go (calls cmd.Execute())
- tests/go/kvtest/cmd/kvtest/main.go (this file)
Pick a single layout:
- Option A (simplest): Keep tests/go/kvtest/main.go and delete tests/go/kvtest/cmd/kvtest/main.go.
- Option B (conventional multi-command): Move root.go under tests/go/kvtest/cmd/kvtest (package cmd), keep this main to call cmd.Execute(), and remove tests/go/kvtest/main.go.
Having both will confuse consumers and CI; with the current syntax error, the module cannot be built.
Run the following to enumerate all main packages under kvtest and confirm you have only one after the change:
🏁 Script executed:
#!/bin/bash
# List all Go files that declare package main under tests/go/kvtest
rg -n '^package main\b' -g 'tests/go/kvtest/**/*.go' -S -C1
Length of output: 247
Remove the duplicate main
entrypoint
The verification script still finds two package main
declarations under tests/go/kvtest
:
- tests/go/kvtest/main.go
- tests/go/kvtest/cmd/kvtest/main.go
You must choose one layout and delete the other to ensure there’s only a single executable entrypoint:
-
Option A (simplest):
• Keeptests/go/kvtest/main.go
(which callscmd.Execute()
).
• Deletetests/go/kvtest/cmd/kvtest/main.go
. -
Option B (conventional multi-command):
• Moveroot.go
undertests/go/kvtest/cmd/kvtest
(packagecmd
).
• Keeptests/go/kvtest/cmd/kvtest/main.go
to callcmd.Execute()
.
• Deletetests/go/kvtest/main.go
.
Maintaining both prevents the module from building and may confuse CI and downstream consumers. Please remove the unwanted main.go
.
🤖 Prompt for AI Agents
In tests/go/kvtest/cmd/kvtest/main.go (lines 1-5) there's a duplicate package
main entrypoint; remove this file so only tests/go/kvtest/main.go remains as the
single executable entrypoint (Option A). After deletion, ensure
tests/go/kvtest/main.go calls cmd.Execute() and no other package/main files
exist under tests/go/kvtest; run `go build` or the test verifier to confirm the
module builds.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||
*/ | ||
package cmd | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
// rootCmd represents the base command when called without any subcommands | ||
var rootCmd = &cobra.Command{ | ||
Use: "kvtest", | ||
Short: "A brief description of your application", | ||
Long: `A longer description that spans multiple lines and likely contains | ||
examples and usage of using your application. For example: | ||
|
||
Cobra is a CLI library for Go that empowers applications. | ||
This application is a tool to generate the needed files | ||
to quickly create a Cobra application.`, | ||
// Uncomment the following line if your bare application | ||
// has an action associated with it: | ||
// Run: func(cmd *cobra.Command, args []string) { }, | ||
} | ||
|
||
// Execute adds all child commands to the root command and sets flags appropriately. | ||
// This is called by main.main(). It only needs to happen once to the rootCmd. | ||
func Execute() { | ||
err := rootCmd.Execute() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func init() { | ||
// Here you will define your flags and configuration settings. | ||
// Cobra supports persistent flags, which, if defined here, | ||
// will be global for your application. | ||
|
||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kvtest.yaml)") | ||
|
||
// Cobra also supports local flags, which will only run | ||
// when this action is called directly. | ||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
package kvtest | ||
/* | ||
Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||
|
||
import ( | ||
v1 "github.com/lnikon/tinykvpp/tests/go/kvtest/proto" | ||
"google.golang.org/grpc" | ||
) | ||
*/ | ||
package main | ||
|
||
import "github.com/lnikon/tinykvpp/tests/go/kvtest/cmd" | ||
|
||
func main() { | ||
let req := v1.GetRequest{} | ||
cmd.Execute() | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package adapters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"google.golang.org/grpc" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"google.golang.org/grpc/credentials/insecure" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/lnikon/tinykvpp/tests/go/kvtest/pkg/core" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pb "github.com/lnikon/tinykvpp/tests/go/kvtest/proto/tinykvpp/v1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type TinyKVPPConfig struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Address string `yaml:"address"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ConnectionTimeout time.Duration `yaml:"connection_timeout"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RequestTimeout time.Duration `yaml:"request_timeout"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MaxRetries int `yaml:"max_retries"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type TinyKVPPAdapter struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
config TinyKVPPConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
conn *grpc.ClientConn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
client pb.TinyKVPPServiceClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var _ core.KVStoreInterface = (*TinyKVPPAdapter)(nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func NewTinyKVPPAdapter(config core.Config) *TinyKVPPAdapter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return &TinyKVPPAdapter{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
func NewTinyKVPPAdapter(config core.Config) *TinyKVPPAdapter { | |
return &TinyKVPPAdapter{} | |
} | |
func NewTinyKVPPAdapter(config core.Config) *TinyKVPPAdapter { | |
a := &TinyKVPPAdapter{} | |
if cfg, ok := config.(TinyKVPPConfig); ok { | |
a.config = cfg | |
} | |
return a | |
} |
🤖 Prompt for AI Agents
In tests/go/kvtest/pkg/adapters/tinykvpp.go around lines 30 to 33, the
NewTinyKVPPAdapter constructor accepts a core.Config but ignores it; either
persist the config on the adapter or remove the unused parameter. Fix option A:
add a config field to TinyKVPPAdapter (e.g., cfg core.Config), set it in the
constructor by returning &TinyKVPPAdapter{cfg: config}, and update usages to
access adapter.cfg as needed. Fix option B: if the adapter does not need config,
remove the parameter from NewTinyKVPPAdapter and update all call sites and tests
to call the zero-arg constructor. Ensure imports and tests compile after the
change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Get: fix typo, use request timeout, and return sentinel for not found
- Typo: “requet” -> “request”.
- Prefer returning a sentinel error (e.g., core.ErrKeyNotFound) so callers can do errors.Is.
- Minor: wrap transport error with %w.
func (a *TinyKVPPAdapter) Get(ctx context.Context, key []byte) ([]byte, error) {
if a.conn == nil {
return nil, fmt.Errorf("not connected to TinyKVPP")
}
- // Apply requet timeout
+ // Apply request timeout
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout)
defer cancel()
req := &pb.GetRequest{Key: string(key)}
resp, err := a.client.Get(ctx, req)
if err != nil {
- return nil, fmt.Errorf("grpc get failed for key %s: %v", key, err)
+ return nil, fmt.Errorf("grpc get failed for key %s: %w", key, err)
}
if !resp.Found {
- return nil, &core.KVError{Op: "get", Err: fmt.Errorf("key not found: %s", string(key))}
+ return nil, core.ErrKeyNotFound
}
return []byte(resp.Value), nil
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (a *TinyKVPPAdapter) Get(ctx context.Context, key []byte) ([]byte, error) { | |
if a.conn == nil { | |
return nil, fmt.Errorf("not connected to TinyKVPP") | |
} | |
// Apply requet timeout | |
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout) | |
defer cancel() | |
req := &pb.GetRequest{Key: string(key)} | |
resp, err := a.client.Get(ctx, req) | |
if err != nil { | |
return nil, fmt.Errorf("grpc get failed for key %s: %v", key, err) | |
} | |
if !resp.Found { | |
return nil, &core.KVError{Op: "get", Err: fmt.Errorf("key not found: %s", string(key))} | |
} | |
return []byte(resp.Value), nil | |
} | |
func (a *TinyKVPPAdapter) Get(ctx context.Context, key []byte) ([]byte, error) { | |
if a.conn == nil { | |
return nil, fmt.Errorf("not connected to TinyKVPP") | |
} | |
// Apply request timeout | |
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout) | |
defer cancel() | |
req := &pb.GetRequest{Key: string(key)} | |
resp, err := a.client.Get(ctx, req) | |
if err != nil { | |
return nil, fmt.Errorf("grpc get failed for key %s: %w", key, err) | |
} | |
if !resp.Found { | |
return nil, core.ErrKeyNotFound | |
} | |
return []byte(resp.Value), nil | |
} |
🤖 Prompt for AI Agents
In tests/go/kvtest/pkg/adapters/tinykvpp.go around lines 61 to 81, fix the
comment typo and improve error handling: correct "requet" to "request"; when
returning a not-found result, return the package sentinel error
(core.ErrKeyNotFound) or wrap it with fmt.Errorf("%w", core.ErrKeyNotFound)
instead of constructing a new core.KVError so callers can use errors.Is; and
wrap the gRPC transport error using the %w verb (fmt.Errorf("grpc get failed for
key %s: %w", string(key), err)) so the underlying error is preserved for
inspection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Put: fix typo, consistent error wrapping, and avoid trailing newline in errors
- Typo: “requet” -> “request”.
- Use %w to preserve cause.
- Don’t append newline in error messages.
func (a *TinyKVPPAdapter) Put(ctx context.Context, key, value []byte) error {
if a.conn == nil {
return fmt.Errorf("not connected to TinyKVPP")
}
- // Apply requet timeout
+ // Apply request timeout
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout)
defer cancel()
req := &pb.PutRequest{Key: string(key), Value: string(value)}
resp, err := a.client.Put(ctx, req)
if err != nil {
- return fmt.Errorf("grpc put failed for key %s: %v\n", key, err)
+ return fmt.Errorf("grpc put failed for key %s: %w", key, err)
}
- if len(resp.Error) > 0 {
- return &core.KVError{Op: "put", Err: fmt.Errorf("put failed: %s", resp.Error)}
+ if len(resp.Error) > 0 { // if you keep the string field
+ return &core.KVError{Op: "put", Err: fmt.Errorf("%s", resp.Error)}
}
if !resp.Success {
return &core.KVError{Op: "put", Err: fmt.Errorf("put operation unsuccessful")}
}
return nil
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (a *TinyKVPPAdapter) Put(ctx context.Context, key, value []byte) error { | |
if a.conn == nil { | |
return fmt.Errorf("not connected to TinyKVPP") | |
} | |
// Apply requet timeout | |
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout) | |
defer cancel() | |
req := &pb.PutRequest{Key: string(key), Value: string(value)} | |
resp, err := a.client.Put(ctx, req) | |
if err != nil { | |
return fmt.Errorf("grpc put failed for key %s: %v\n", key, err) | |
} | |
if len(resp.Error) > 0 { | |
return &core.KVError{Op: "put", Err: fmt.Errorf("put failed: %s", resp.Error)} | |
} | |
if !resp.Success { | |
return &core.KVError{Op: "put", Err: fmt.Errorf("put operation unsuccessful")} | |
} | |
return nil | |
} | |
func (a *TinyKVPPAdapter) Put(ctx context.Context, key, value []byte) error { | |
if a.conn == nil { | |
return fmt.Errorf("not connected to TinyKVPP") | |
} | |
// Apply request timeout | |
ctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout) | |
defer cancel() | |
req := &pb.PutRequest{Key: string(key), Value: string(value)} | |
resp, err := a.client.Put(ctx, req) | |
if err != nil { | |
return fmt.Errorf("grpc put failed for key %s: %w", key, err) | |
} | |
if len(resp.Error) > 0 { // if you keep the string field | |
return &core.KVError{Op: "put", Err: fmt.Errorf("%s", resp.Error)} | |
} | |
if !resp.Success { | |
return &core.KVError{Op: "put", Err: fmt.Errorf("put operation unsuccessful")} | |
} | |
return nil | |
} |
🤖 Prompt for AI Agents
In tests/go/kvtest/pkg/adapters/tinykvpp.go around lines 83-107, fix the typo in
the inline comment ("requet" -> "request"), stop appending a newline to the gRPC
error message, and wrap the underlying gRPC error with %w to preserve the causal
error (use fmt.Errorf("grpc put failed for key %s: %w", string(key), err)). Keep
other KVError returns as-is but ensure any fmt.Errorf used for wrapping uses %w
consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
HealthCheck should validate RPC reachability and honor timeouts
Currently it ignores the RPC result, so Connect always returns nil if dialing succeeds. Propagate RPC error to surface server unavailability.
func (a *TinyKVPPAdapter) HealthCheck(ctx context.Context) error {
if a.conn == nil {
return fmt.Errorf("not connected")
}
- testKey := "__health_check__"
- a.client.Get(ctx, &pb.GetRequest{Key: testKey})
-
- return nil
+ // Short probe
+ if a.config.RequestTimeout <= 0 {
+ a.config.RequestTimeout = 1 * time.Second
+ }
+ cctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout)
+ defer cancel()
+ _, err := a.client.Get(cctx, &pb.GetRequest{Key: "__health_check__"})
+ // Presence isn’t required; only transport success matters.
+ if err != nil {
+ return fmt.Errorf("health check RPC failed: %w", err)
+ }
+ return nil
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (a *TinyKVPPAdapter) HealthCheck(ctx context.Context) error { | |
if a.conn == nil { | |
return fmt.Errorf("not connected") | |
} | |
testKey := "__health_check__" | |
a.client.Get(ctx, &pb.GetRequest{Key: testKey}) | |
return nil | |
} | |
func (a *TinyKVPPAdapter) HealthCheck(ctx context.Context) error { | |
if a.conn == nil { | |
return fmt.Errorf("not connected") | |
} | |
// Short probe | |
if a.config.RequestTimeout <= 0 { | |
a.config.RequestTimeout = 1 * time.Second | |
} | |
cctx, cancel := context.WithTimeout(ctx, a.config.RequestTimeout) | |
defer cancel() | |
_, err := a.client.Get(cctx, &pb.GetRequest{Key: "__health_check__"}) | |
// Presence isn’t required; only transport success matters. | |
if err != nil { | |
return fmt.Errorf("health check RPC failed: %w", err) | |
} | |
return nil | |
} |
🤖 Prompt for AI Agents
In tests/go/kvtest/pkg/adapters/tinykvpp.go around lines 109-118, the
HealthCheck currently ignores the RPC result so dialing success always makes
Connect appear healthy; capture and propagate the error from a.client.Get(ctx,
&pb.GetRequest{Key: testKey}) and return it (so RPC failures surface to callers)
and ensure the call uses the provided ctx so any deadlines/timeouts are honored;
keep the existing conn nil check and return fmt.Errorf("not connected") when
appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Peers should not use 0.0.0.0; make empty for standalone or use real endpoints
"0.0.0.0" is valid for binding but invalid as a remote peer address. In standalone mode, use an empty list; in clustered mode, list concrete host:port values for other nodes.
Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents