From e04f00c3f258babd35e6947e5f2be09fd3231816 Mon Sep 17 00:00:00 2001 From: Mateo Presa Castro Date: Mon, 17 Nov 2025 07:47:14 +0100 Subject: [PATCH 1/4] chore: start setting up deploy --- Makefile | 10 +- api/health.pb.go | 233 ++++++++++++++++++ api/health.proto | 23 ++ api/health_grpc.pb.go | 163 ++++++++++++ api/kv.pb.go | 178 ++++++------- api/kv_grpc.pb.go | 6 +- deploy/mokv/.helmignore | 23 ++ deploy/mokv/Chart.yaml | 24 ++ deploy/mokv/templates/_helpers.tpl | 62 +++++ deploy/mokv/templates/statefulset.yaml | 69 ++++++ .../mokv/templates/tests/test-connection.yaml | 15 ++ deploy/mokv/values.yaml | 161 ++++++++++++ server/server.go | 8 +- 13 files changed, 871 insertions(+), 104 deletions(-) create mode 100644 api/health.pb.go create mode 100644 api/health.proto create mode 100644 api/health_grpc.pb.go create mode 100644 deploy/mokv/.helmignore create mode 100644 deploy/mokv/Chart.yaml create mode 100644 deploy/mokv/templates/_helpers.tpl create mode 100644 deploy/mokv/templates/statefulset.yaml create mode 100644 deploy/mokv/templates/tests/test-connection.yaml create mode 100644 deploy/mokv/values.yaml diff --git a/Makefile b/Makefile index 059a1f4..81345e5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ +TAG ?= 0.0.1 + .PHONY: compile compile: - protoc internal/api/*.proto \ + protoc ./api/*.proto \ --go_out=. \ --go-grpc_out=. \ --go_opt=paths=source_relative \ @@ -25,4 +27,8 @@ perf: .PHONY: perf-long perf-long: - -go test -bench=. -benchtime=60s ./mokv -benchmem -run=^# \ No newline at end of file + -go test -bench=. -benchtime=60s ./mokv -benchmem -run=^# + +.PHONY: build-docker +build-docker: + docker build -t github.com/dynamic-calm/mokv:$(TAG) . \ No newline at end of file diff --git a/api/health.pb.go b/api/health.pb.go new file mode 100644 index 0000000..0e24a2d --- /dev/null +++ b/api/health.pb.go @@ -0,0 +1,233 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.8 +// protoc v6.32.0 +// source: api/health.proto + +package api + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HealthCheckResponse_ServingStatus int32 + +const ( + HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0 + HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1 + HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2 +) + +// Enum value maps for HealthCheckResponse_ServingStatus. +var ( + HealthCheckResponse_ServingStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SERVING", + 2: "NOT_SERVING", + } + HealthCheckResponse_ServingStatus_value = map[string]int32{ + "UNKNOWN": 0, + "SERVING": 1, + "NOT_SERVING": 2, + } +) + +func (x HealthCheckResponse_ServingStatus) Enum() *HealthCheckResponse_ServingStatus { + p := new(HealthCheckResponse_ServingStatus) + *p = x + return p +} + +func (x HealthCheckResponse_ServingStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.EnumDescriptor { + return file_api_health_proto_enumTypes[0].Descriptor() +} + +func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType { + return &file_api_health_proto_enumTypes[0] +} + +func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use HealthCheckResponse_ServingStatus.Descriptor instead. +func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) { + return file_api_health_proto_rawDescGZIP(), []int{1, 0} +} + +type HealthCheckRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckRequest) Reset() { + *x = HealthCheckRequest{} + mi := &file_api_health_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckRequest) ProtoMessage() {} + +func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_health_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. +func (*HealthCheckRequest) Descriptor() ([]byte, []int) { + return file_api_health_proto_rawDescGZIP(), []int{0} +} + +func (x *HealthCheckRequest) GetService() string { + if x != nil { + return x.Service + } + return "" +} + +type HealthCheckResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckResponse) Reset() { + *x = HealthCheckResponse{} + mi := &file_api_health_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckResponse) ProtoMessage() {} + +func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_health_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return file_api_health_proto_rawDescGZIP(), []int{1} +} + +func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus { + if x != nil { + return x.Status + } + return HealthCheckResponse_UNKNOWN +} + +var File_api_health_proto protoreflect.FileDescriptor + +const file_api_health_proto_rawDesc = "" + + "\n" + + "\x10api/health.proto\x12\x0egrpc.health.v1\".\n" + + "\x12HealthCheckRequest\x12\x18\n" + + "\aservice\x18\x01 \x01(\tR\aservice\"\x9c\x01\n" + + "\x13HealthCheckResponse\x12I\n" + + "\x06status\x18\x01 \x01(\x0e21.grpc.health.v1.HealthCheckResponse.ServingStatusR\x06status\":\n" + + "\rServingStatus\x12\v\n" + + "\aUNKNOWN\x10\x00\x12\v\n" + + "\aSERVING\x10\x01\x12\x0f\n" + + "\vNOT_SERVING\x10\x022\xae\x01\n" + + "\x06Health\x12P\n" + + "\x05Check\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse\x12R\n" + + "\x05Watch\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse0\x01B\"Z github.com/dynamic-calm/mokv/apib\x06proto3" + +var ( + file_api_health_proto_rawDescOnce sync.Once + file_api_health_proto_rawDescData []byte +) + +func file_api_health_proto_rawDescGZIP() []byte { + file_api_health_proto_rawDescOnce.Do(func() { + file_api_health_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_health_proto_rawDesc), len(file_api_health_proto_rawDesc))) + }) + return file_api_health_proto_rawDescData +} + +var file_api_health_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_api_health_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_api_health_proto_goTypes = []any{ + (HealthCheckResponse_ServingStatus)(0), // 0: grpc.health.v1.HealthCheckResponse.ServingStatus + (*HealthCheckRequest)(nil), // 1: grpc.health.v1.HealthCheckRequest + (*HealthCheckResponse)(nil), // 2: grpc.health.v1.HealthCheckResponse +} +var file_api_health_proto_depIdxs = []int32{ + 0, // 0: grpc.health.v1.HealthCheckResponse.status:type_name -> grpc.health.v1.HealthCheckResponse.ServingStatus + 1, // 1: grpc.health.v1.Health.Check:input_type -> grpc.health.v1.HealthCheckRequest + 1, // 2: grpc.health.v1.Health.Watch:input_type -> grpc.health.v1.HealthCheckRequest + 2, // 3: grpc.health.v1.Health.Check:output_type -> grpc.health.v1.HealthCheckResponse + 2, // 4: grpc.health.v1.Health.Watch:output_type -> grpc.health.v1.HealthCheckResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_api_health_proto_init() } +func file_api_health_proto_init() { + if File_api_health_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_health_proto_rawDesc), len(file_api_health_proto_rawDesc)), + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_api_health_proto_goTypes, + DependencyIndexes: file_api_health_proto_depIdxs, + EnumInfos: file_api_health_proto_enumTypes, + MessageInfos: file_api_health_proto_msgTypes, + }.Build() + File_api_health_proto = out.File + file_api_health_proto_goTypes = nil + file_api_health_proto_depIdxs = nil +} diff --git a/api/health.proto b/api/health.proto new file mode 100644 index 0000000..06d1f53 --- /dev/null +++ b/api/health.proto @@ -0,0 +1,23 @@ +syntax = "proto3" ; + +option go_package = "github.com/dynamic-calm/mokv/api"; + +package grpc.health.v1; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); +} \ No newline at end of file diff --git a/api/health_grpc.pb.go b/api/health_grpc.pb.go new file mode 100644 index 0000000..bc08242 --- /dev/null +++ b/api/health_grpc.pb.go @@ -0,0 +1,163 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v6.32.0 +// source: api/health.proto + +package api + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Health_Check_FullMethodName = "/grpc.health.v1.Health/Check" + Health_Watch_FullMethodName = "/grpc.health.v1.Health/Watch" +) + +// HealthClient is the client API for Health service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HealthClient interface { + Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) + Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckResponse], error) +} + +type healthClient struct { + cc grpc.ClientConnInterface +} + +func NewHealthClient(cc grpc.ClientConnInterface) HealthClient { + return &healthClient{cc} +} + +func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HealthCheckResponse) + err := c.cc.Invoke(ctx, Health_Check_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *healthClient) Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Health_ServiceDesc.Streams[0], Health_Watch_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[HealthCheckRequest, HealthCheckResponse]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Health_WatchClient = grpc.ServerStreamingClient[HealthCheckResponse] + +// HealthServer is the server API for Health service. +// All implementations must embed UnimplementedHealthServer +// for forward compatibility. +type HealthServer interface { + Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) + Watch(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckResponse]) error + mustEmbedUnimplementedHealthServer() +} + +// UnimplementedHealthServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedHealthServer struct{} + +func (UnimplementedHealthServer) Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Check not implemented") +} +func (UnimplementedHealthServer) Watch(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckResponse]) error { + return status.Errorf(codes.Unimplemented, "method Watch not implemented") +} +func (UnimplementedHealthServer) mustEmbedUnimplementedHealthServer() {} +func (UnimplementedHealthServer) testEmbeddedByValue() {} + +// UnsafeHealthServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HealthServer will +// result in compilation errors. +type UnsafeHealthServer interface { + mustEmbedUnimplementedHealthServer() +} + +func RegisterHealthServer(s grpc.ServiceRegistrar, srv HealthServer) { + // If the following call pancis, it indicates UnimplementedHealthServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Health_ServiceDesc, srv) +} + +func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HealthCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HealthServer).Check(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Health_Check_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HealthServer).Check(ctx, req.(*HealthCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Health_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(HealthCheckRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(HealthServer).Watch(m, &grpc.GenericServerStream[HealthCheckRequest, HealthCheckResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Health_WatchServer = grpc.ServerStreamingServer[HealthCheckResponse] + +// Health_ServiceDesc is the grpc.ServiceDesc for Health service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Health_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "grpc.health.v1.Health", + HandlerType: (*HealthServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Check", + Handler: _Health_Check_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Watch", + Handler: _Health_Watch_Handler, + ServerStreams: true, + }, + }, + Metadata: "api/health.proto", +} diff --git a/api/kv.pb.go b/api/kv.pb.go index b862830..1fbb017 100644 --- a/api/kv.pb.go +++ b/api/kv.pb.go @@ -1,8 +1,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.5 -// protoc v5.29.3 -// source: internal/api/kv.proto +// protoc-gen-go v1.36.8 +// protoc v6.32.0 +// source: api/kv.proto package api @@ -31,7 +31,7 @@ type GetRequest struct { func (x *GetRequest) Reset() { *x = GetRequest{} - mi := &file_internal_api_kv_proto_msgTypes[0] + mi := &file_api_kv_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -43,7 +43,7 @@ func (x *GetRequest) String() string { func (*GetRequest) ProtoMessage() {} func (x *GetRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[0] + mi := &file_api_kv_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -56,7 +56,7 @@ func (x *GetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRequest.ProtoReflect.Descriptor instead. func (*GetRequest) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{0} + return file_api_kv_proto_rawDescGZIP(), []int{0} } func (x *GetRequest) GetKey() string { @@ -76,7 +76,7 @@ type GetResponse struct { func (x *GetResponse) Reset() { *x = GetResponse{} - mi := &file_internal_api_kv_proto_msgTypes[1] + mi := &file_api_kv_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -88,7 +88,7 @@ func (x *GetResponse) String() string { func (*GetResponse) ProtoMessage() {} func (x *GetResponse) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[1] + mi := &file_api_kv_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -101,7 +101,7 @@ func (x *GetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetResponse.ProtoReflect.Descriptor instead. func (*GetResponse) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{1} + return file_api_kv_proto_rawDescGZIP(), []int{1} } func (x *GetResponse) GetKey() string { @@ -128,7 +128,7 @@ type SetRequest struct { func (x *SetRequest) Reset() { *x = SetRequest{} - mi := &file_internal_api_kv_proto_msgTypes[2] + mi := &file_api_kv_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -140,7 +140,7 @@ func (x *SetRequest) String() string { func (*SetRequest) ProtoMessage() {} func (x *SetRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[2] + mi := &file_api_kv_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -153,7 +153,7 @@ func (x *SetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetRequest.ProtoReflect.Descriptor instead. func (*SetRequest) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{2} + return file_api_kv_proto_rawDescGZIP(), []int{2} } func (x *SetRequest) GetKey() string { @@ -179,7 +179,7 @@ type SetResponse struct { func (x *SetResponse) Reset() { *x = SetResponse{} - mi := &file_internal_api_kv_proto_msgTypes[3] + mi := &file_api_kv_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -191,7 +191,7 @@ func (x *SetResponse) String() string { func (*SetResponse) ProtoMessage() {} func (x *SetResponse) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[3] + mi := &file_api_kv_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -204,7 +204,7 @@ func (x *SetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetResponse.ProtoReflect.Descriptor instead. func (*SetResponse) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{3} + return file_api_kv_proto_rawDescGZIP(), []int{3} } func (x *SetResponse) GetOk() bool { @@ -223,7 +223,7 @@ type DeleteRequest struct { func (x *DeleteRequest) Reset() { *x = DeleteRequest{} - mi := &file_internal_api_kv_proto_msgTypes[4] + mi := &file_api_kv_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -235,7 +235,7 @@ func (x *DeleteRequest) String() string { func (*DeleteRequest) ProtoMessage() {} func (x *DeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[4] + mi := &file_api_kv_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -248,7 +248,7 @@ func (x *DeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead. func (*DeleteRequest) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{4} + return file_api_kv_proto_rawDescGZIP(), []int{4} } func (x *DeleteRequest) GetKey() string { @@ -267,7 +267,7 @@ type DeleteResponse struct { func (x *DeleteResponse) Reset() { *x = DeleteResponse{} - mi := &file_internal_api_kv_proto_msgTypes[5] + mi := &file_api_kv_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -279,7 +279,7 @@ func (x *DeleteResponse) String() string { func (*DeleteResponse) ProtoMessage() {} func (x *DeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[5] + mi := &file_api_kv_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -292,7 +292,7 @@ func (x *DeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteResponse.ProtoReflect.Descriptor instead. func (*DeleteResponse) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{5} + return file_api_kv_proto_rawDescGZIP(), []int{5} } func (x *DeleteResponse) GetOk() bool { @@ -313,7 +313,7 @@ type Server struct { func (x *Server) Reset() { *x = Server{} - mi := &file_internal_api_kv_proto_msgTypes[6] + mi := &file_api_kv_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -325,7 +325,7 @@ func (x *Server) String() string { func (*Server) ProtoMessage() {} func (x *Server) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[6] + mi := &file_api_kv_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -338,7 +338,7 @@ func (x *Server) ProtoReflect() protoreflect.Message { // Deprecated: Use Server.ProtoReflect.Descriptor instead. func (*Server) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{6} + return file_api_kv_proto_rawDescGZIP(), []int{6} } func (x *Server) GetId() string { @@ -371,7 +371,7 @@ type GetServersResponse struct { func (x *GetServersResponse) Reset() { *x = GetServersResponse{} - mi := &file_internal_api_kv_proto_msgTypes[7] + mi := &file_api_kv_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -383,7 +383,7 @@ func (x *GetServersResponse) String() string { func (*GetServersResponse) ProtoMessage() {} func (x *GetServersResponse) ProtoReflect() protoreflect.Message { - mi := &file_internal_api_kv_proto_msgTypes[7] + mi := &file_api_kv_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -396,7 +396,7 @@ func (x *GetServersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetServersResponse.ProtoReflect.Descriptor instead. func (*GetServersResponse) Descriptor() ([]byte, []int) { - return file_internal_api_kv_proto_rawDescGZIP(), []int{7} + return file_api_kv_proto_rawDescGZIP(), []int{7} } func (x *GetServersResponse) GetServers() []*Server { @@ -406,73 +406,55 @@ func (x *GetServersResponse) GetServers() []*Server { return nil } -var File_internal_api_kv_proto protoreflect.FileDescriptor - -var file_internal_api_kv_proto_rawDesc = string([]byte{ - 0x0a, 0x15, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, - 0x76, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, - 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x0a, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x0b, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x34, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0x21, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0x50, 0x0a, 0x06, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x3b, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x32, 0x88, 0x02, 0x0a, 0x02, 0x4b, 0x56, - 0x12, 0x2a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x03, - 0x53, 0x65, 0x74, 0x12, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x12, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34, 0x0a, - 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x3f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x2d, 0x63, 0x61, 0x6c, 0x6d, 0x2f, - 0x6d, 0x6f, 0x6b, 0x76, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x70, - 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -}) +var File_api_kv_proto protoreflect.FileDescriptor + +const file_api_kv_proto_rawDesc = "" + + "\n" + + "\fapi/kv.proto\x12\x03api\x1a\x1bgoogle/protobuf/empty.proto\"\x1e\n" + + "\n" + + "GetRequest\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\"5\n" + + "\vGetResponse\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\fR\x05value\"4\n" + + "\n" + + "SetRequest\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\fR\x05value\"\x1d\n" + + "\vSetResponse\x12\x0e\n" + + "\x02ok\x18\x01 \x01(\bR\x02ok\"!\n" + + "\rDeleteRequest\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\" \n" + + "\x0eDeleteResponse\x12\x0e\n" + + "\x02ok\x18\x01 \x01(\bR\x02ok\"P\n" + + "\x06Server\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x19\n" + + "\brpc_addr\x18\x02 \x01(\tR\arpcAddr\x12\x1b\n" + + "\tis_leader\x18\x03 \x01(\bR\bisLeader\";\n" + + "\x12GetServersResponse\x12%\n" + + "\aservers\x18\x01 \x03(\v2\v.api.ServerR\aservers2\x88\x02\n" + + "\x02KV\x12*\n" + + "\x03Get\x12\x0f.api.GetRequest\x1a\x10.api.GetResponse\"\x00\x12*\n" + + "\x03Set\x12\x0f.api.SetRequest\x1a\x10.api.SetResponse\"\x00\x123\n" + + "\x06Delete\x12\x12.api.DeleteRequest\x1a\x13.api.DeleteResponse\"\x00\x124\n" + + "\x04List\x12\x16.google.protobuf.Empty\x1a\x10.api.GetResponse\"\x000\x01\x12?\n" + + "\n" + + "GetServers\x12\x16.google.protobuf.Empty\x1a\x17.api.GetServersResponse\"\x00B\"Z github.com/dynamic-calm/mokv/apib\x06proto3" var ( - file_internal_api_kv_proto_rawDescOnce sync.Once - file_internal_api_kv_proto_rawDescData []byte + file_api_kv_proto_rawDescOnce sync.Once + file_api_kv_proto_rawDescData []byte ) -func file_internal_api_kv_proto_rawDescGZIP() []byte { - file_internal_api_kv_proto_rawDescOnce.Do(func() { - file_internal_api_kv_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_internal_api_kv_proto_rawDesc), len(file_internal_api_kv_proto_rawDesc))) +func file_api_kv_proto_rawDescGZIP() []byte { + file_api_kv_proto_rawDescOnce.Do(func() { + file_api_kv_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_kv_proto_rawDesc), len(file_api_kv_proto_rawDesc))) }) - return file_internal_api_kv_proto_rawDescData + return file_api_kv_proto_rawDescData } -var file_internal_api_kv_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_internal_api_kv_proto_goTypes = []any{ +var file_api_kv_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_api_kv_proto_goTypes = []any{ (*GetRequest)(nil), // 0: api.GetRequest (*GetResponse)(nil), // 1: api.GetResponse (*SetRequest)(nil), // 2: api.SetRequest @@ -483,7 +465,7 @@ var file_internal_api_kv_proto_goTypes = []any{ (*GetServersResponse)(nil), // 7: api.GetServersResponse (*emptypb.Empty)(nil), // 8: google.protobuf.Empty } -var file_internal_api_kv_proto_depIdxs = []int32{ +var file_api_kv_proto_depIdxs = []int32{ 6, // 0: api.GetServersResponse.servers:type_name -> api.Server 0, // 1: api.KV.Get:input_type -> api.GetRequest 2, // 2: api.KV.Set:input_type -> api.SetRequest @@ -502,26 +484,26 @@ var file_internal_api_kv_proto_depIdxs = []int32{ 0, // [0:1] is the sub-list for field type_name } -func init() { file_internal_api_kv_proto_init() } -func file_internal_api_kv_proto_init() { - if File_internal_api_kv_proto != nil { +func init() { file_api_kv_proto_init() } +func file_api_kv_proto_init() { + if File_api_kv_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_internal_api_kv_proto_rawDesc), len(file_internal_api_kv_proto_rawDesc)), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_kv_proto_rawDesc), len(file_api_kv_proto_rawDesc)), NumEnums: 0, NumMessages: 8, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_internal_api_kv_proto_goTypes, - DependencyIndexes: file_internal_api_kv_proto_depIdxs, - MessageInfos: file_internal_api_kv_proto_msgTypes, + GoTypes: file_api_kv_proto_goTypes, + DependencyIndexes: file_api_kv_proto_depIdxs, + MessageInfos: file_api_kv_proto_msgTypes, }.Build() - File_internal_api_kv_proto = out.File - file_internal_api_kv_proto_goTypes = nil - file_internal_api_kv_proto_depIdxs = nil + File_api_kv_proto = out.File + file_api_kv_proto_goTypes = nil + file_api_kv_proto_depIdxs = nil } diff --git a/api/kv_grpc.pb.go b/api/kv_grpc.pb.go index ce4658f..4ddd925 100644 --- a/api/kv_grpc.pb.go +++ b/api/kv_grpc.pb.go @@ -1,8 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.3 -// source: internal/api/kv.proto +// - protoc v6.32.0 +// source: api/kv.proto package api @@ -274,5 +274,5 @@ var KV_ServiceDesc = grpc.ServiceDesc{ ServerStreams: true, }, }, - Metadata: "internal/api/kv.proto", + Metadata: "api/kv.proto", } diff --git a/deploy/mokv/.helmignore b/deploy/mokv/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/deploy/mokv/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deploy/mokv/Chart.yaml b/deploy/mokv/Chart.yaml new file mode 100644 index 0000000..a9c2f73 --- /dev/null +++ b/deploy/mokv/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: mokv +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/deploy/mokv/templates/_helpers.tpl b/deploy/mokv/templates/_helpers.tpl new file mode 100644 index 0000000..4407fe1 --- /dev/null +++ b/deploy/mokv/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "mokv.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "mokv.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "mokv.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "mokv.labels" -}} +helm.sh/chart: {{ include "mokv.chart" . }} +{{ include "mokv.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "mokv.selectorLabels" -}} +app.kubernetes.io/name: {{ include "mokv.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "mokv.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "mokv.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/deploy/mokv/templates/statefulset.yaml b/deploy/mokv/templates/statefulset.yaml new file mode 100644 index 0000000..b4c6fc2 --- /dev/null +++ b/deploy/mokv/templates/statefulset.yaml @@ -0,0 +1,69 @@ +# START: initial +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "mokv.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{ include "mokv.labels" . | nindent 4 }} +spec: + selector: + matchLabels: {{ include "mokv.selectorLabels" . | nindent 6 }} + serviceName: {{ include "mokv.fullname" . }} + replicas: {{ .Values.replicas }} + template: + metadata: + name: {{ include "mokv.fullname" . }} + labels: {{ include "mokv.labels" . | nindent 8 }} + spec: + initContainers: + - name: {{ include "mokv.fullname" . }}-config-init + image: busybox + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - |- + ID=$(echo $HOSTNAME | rev | cut -d- -f1 | rev) + cat > /var/run/mokv/config.yaml < Date: Mon, 17 Nov 2025 21:34:49 +0100 Subject: [PATCH 2/4] feat: deploy with kubernetes, update readme and makefile --- Makefile | 60 ++++++- README.md | 153 ++++++++-------- api/health.pb.go | 34 ++-- api/health.proto | 2 +- api/health_grpc.pb.go | 6 +- cmd/test_kv.go | 54 ++++++ deploy/mokv/templates/service.yaml | 22 +++ deploy/mokv/templates/statefulset.yaml | 27 ++- .../mokv/templates/tests/test-connection.yaml | 2 +- deploy/mokv/values.yaml | 166 +----------------- kv/kv.go | 12 +- kv/kv_test.go | 10 +- mokv/mokv.go | 45 +++-- server/server.go | 1 - 14 files changed, 281 insertions(+), 313 deletions(-) create mode 100644 cmd/test_kv.go create mode 100644 deploy/mokv/templates/service.yaml diff --git a/Makefile b/Makefile index 81345e5..7ba102e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -TAG ?= 0.0.1 +VERSION ?= latest +IMAGE_NAME = mokv +IMAGE_TAG = $(IMAGE_NAME):$(VERSION) .PHONY: compile compile: @@ -13,10 +15,6 @@ compile: test: go test -cover -race ./... -.PHONY: start -start: - go run . - .PHONY: build build: go build -o bin/mokv . @@ -29,6 +27,52 @@ perf: perf-long: -go test -bench=. -benchtime=60s ./mokv -benchmem -run=^# -.PHONY: build-docker -build-docker: - docker build -t github.com/dynamic-calm/mokv:$(TAG) . \ No newline at end of file +.PHONY: docker-build +docker-build: + docker build -t $(IMAGE_TAG) . + +.PHONY: kind-load +kind-load: + kind load docker-image $(IMAGE_TAG) + +.PHONY: deploy +deploy: + helm install mokv deploy/mokv + +.PHONY: upgrade +upgrade: + helm upgrade mokv deploy/mokv + +.PHONY: redeploy +redeploy: docker-build kind-load + kubectl rollout restart statefulset mokv + +.PHONY: clean +clean: + helm uninstall mokv && \ + kubectl delete pvc datadir-mokv-0 datadir-mokv-1 datadir-mokv-2 + +.PHONY: logs +logs: + kubectl logs -f mokv-0 + +.PHONY: status +status: + kubectl get pods -l app.kubernetes.io/name=mokv + +.PHONY: start +start: + @echo "Starting up mokv..." + @kind get clusters | grep -q kind || (echo " Creating kind cluster..." && kind create cluster) + @echo "Building Docker image..." + @$(MAKE) docker-build + @echo "Loading image into kind..." + @$(MAKE) kind-load + @helm list | grep -q mokv && (echo "♻ Upgrading existing deployment..." && $(MAKE) upgrade) || (echo "Deploying mokv..." && $(MAKE) deploy) + @echo "Waiting for pods to be ready..." + @kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=mokv --timeout=120s + @echo "mokv is running!" + @kubectl get pods -l app.kubernetes.io/name=mokv + @echo "" + @echo "Run 'kubectl port-forward pod/mokv-0 9400:8400' to access the cluster" + @echo "Then test with: go run cmd/test_kv.go -addr localhost:9400" \ No newline at end of file diff --git a/README.md b/README.md index fb421dd..f681039 100644 --- a/README.md +++ b/README.md @@ -1,127 +1,116 @@ # mökv -`mökv` is a distributed, in-memory key-value store. It utilizes [`Raft`](https://github.com/hashicorp/raft) for consensus, [`serf`](https://github.com/hashicorp/serf) for discvoery, and [`gRPC`](https://github.com/grpc/grpc-go) for communication. +`mökv` is a distributed, in-memory key-value store built with [`Raft`](https://github.com/hashicorp/raft) for consensus, [`Serf`](https://github.com/hashicorp/serf) for discovery, and [`gRPC`](https://github.com/grpc/grpc-go) for communication. ## Features -- Distributed Architecture: Data is replicated across multiple nodes for fault tolerance. -- In-Memory Storage: Provides fast read and write operations. -- `Raft` Consensus: Ensures data consistency across the cluster. -- `gRPC` Interface: Offers a well-defined `API` for interacting with the store. -- Metrics: Exposes `Prometheus` metrics for monitoring cluster health and performance. -- Service Discovery: Uses `serf` for automatic node discovery and membership management. -- Load Balancing: Implements `gRPC` client-side load balancing, directing write operations to the leader and read operations to followers. +- **Distributed & Fault-Tolerant**: Data replicated across multiple nodes using Raft consensus +- **In-Memory Storage**: Fast read/write operations with persistent snapshots +- **Service Discovery**: Automatic node discovery and membership via Serf +- **Smart Load Balancing**: Client-side routing directs writes to leader, reads to followers -## Getting Started - -To run `mökv`: +## Quick Start ### Prerequisites -- [`Go`](https://go.dev/dl/) -- [`ghz`](https://ghz.sh/) (for performance testing. Optional) +- [`Docker`](https://www.docker.com/) +- [`kind`](https://kind.sigs.k8s.io/) (for local Kubernetes) +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) +- [`Helm`](https://helm.sh/) -### Installation +### Run it locally -1. Clone the repository: +```bash +make dev +``` - ```bash - git clone git@github.com:dynamic-calm/mokv.git - cd mokv - ``` +This will: -2. Compile the code: +1. Create a kind cluster (if needed) +2. Build the Docker image +3. Load it into kind +4. Deploy with Helm +5. Wait for all pods to be ready - ```bash - make build - ``` +Then test it: - This will create an executable binary `mokv` in the `bin/` directory. +```bash +kubectl port-forward pod/mokv-0 9400:8400 +# In another terminal: +go run cmd/get_servers.go -addr localhost:9400 +``` ### Configuration -Configuration is done through command-line flags or a configuration file. A sample configuration file (`example/config.yaml`) is provided. +Customize via `deploy/mokv/values.yaml` or override during installation: -Here's an example `config.yaml`: - -```yaml -data-dir: /tmp/mokv-data -node-name: node1 -bind-addr: 127.0.0.1:8401 -rpc-port: 8400 -start-join-addrs: [] -bootstrap: true -metrics-port: 4000 +```bash +helm install mokv deploy/mokv --set replicas=5 --set storage=2Gi ``` -### Running mökv - -1. Start the first node: - - ```bash - bin/mokv --config-file example/config.yaml - ``` - -2. Start additional nodes: - - Modify the `example/config.yaml` file with the appropriate `node-name`, `bind-addr`, and `rpc-port`. Crucially, set `start-join-addrs` to the address of the first node (e.g., `127.0.0.1:8401`). Also set `bootstrap: false` for the additional nodes. Then, run the command again: +Default values: - ```bash - bin/mokv --config-file example/config2.yaml # Example config for the second node - ``` - - Refer to [`example/start_nodes.sh`](example/start_nodes.sh) for a convenient script to start a cluster. +```yaml +replicas: 3 # Number of nodes +storage: 1Gi # Persistent volume size per node +rpcPort: 8400 # gRPC port +serfPort: 8401 # Serf discovery port +``` -## Usage +## API -`mökv` exposes a `gRPC` `API` defined in `internal/api/kv.proto`. You can use a `gRPC` client to interact with the store. +`mökv` exposes a gRPC API defined in `api/kv.proto`: ```proto service KV { - rpc Get(GetRequest) returns (GetResponse) {} - rpc Set(SetRequest) returns (SetResponse) {} - rpc Delete(DeleteRequest) returns (DeleteResponse) {} - rpc List(google.protobuf.Empty) returns (stream GetResponse) {} - rpc GetServers(google.protobuf.Empty) returns (GetServersResponse){} + rpc Get(GetRequest) returns (GetResponse); + rpc Set(SetRequest) returns (SetResponse); + rpc Delete(DeleteRequest) returns (DeleteResponse); + rpc List(google.protobuf.Empty) returns (stream GetResponse); + rpc GetServers(google.protobuf.Empty) returns (GetServersResponse); } ``` -## How it Works: Core Components and Data Flow - -`mökv` combines `Serf` for node discovery and `Raft` for consistent data replication. Here's how the key components interact: +## Architecture -- `Serf`: Dynamic Membership: `Serf` uses `UDP` to monitor cluster membership. When a node joins, the `serf.EventMemberJoin` event triggers the `Join` function ([`internal/kv/kv.go`](/kv/kv.go)), adding the node as a `Raft` voter. This ensures the `Raft` cluster reflects the current active nodes. +**Raft Consensus**: Ensures strong consistency with leader-based replication. Writes go through the leader and are replicated to followers. -- `Raft`: Consensus and the FSM: `Raft` guarantees data consistency. One node is `Leader`, handling all write operations. Write operations become `Raft` log entries, replicated to `Followers`. The _Finite State Machine (`FSM`)_ is the core of `Raft's` operation: +**Serf Discovery**: Nodes automatically discover each other via gossip protocol. When a node joins via Serf, it's added as a Raft voter. - - Applying Log Entries: When a log entry is committed (acknowledged by a quorum), the `Apply` method of the `FSM` (in `internal/kv/kv.go`) is invoked. The `Apply` method handles different request types: +**Client-Side Load Balancing**: Custom gRPC resolver and picker route: - - Set Request: Updates the in-memory key-value store (`kv.store`) with the new key-value pair. - - Delete Request: Removes the specified key from the in-memory store. +- **Writes** (`Set`, `Delete`) → Leader +- **Reads** (`Get`, `List`) → Followers (load balanced) - - Data Flow for Writes: `gRPC` -> `Raft Leader` -> `Log Entry` -> `Replication to Followers` -> `FSM Apply` -> `kv.store`. +**Kubernetes Components**: -- Persistence (`raft-boltdb`): `mökv` uses `raft-boltdb` to persist Raft's log, stable state, and periodic snapshots to disk. This enables recovery after node failures. +- **StatefulSet**: Stable network identities (mokv-0, mokv-1, mokv-2) +- **Headless Service**: Direct pod-to-pod communication via FQDNs +- **PersistentVolumeClaims**: Durable storage for Raft logs and snapshots +- **Init Container**: Auto-configures each pod (bootstrap vs join) - - Snapshotting: The `FSM's` `Snapshot` method creates a snapshot of the current in-memory state. - - Restoring State: After a crash, the `FSM's` `Restore` method loads the latest snapshot and replays any subsequent log entries, reconstructing the in-memory `kv.store` to a consistent state. This entire process happens automatically when `setupRaft` is called during startup. +## Management -## gRPC +**Update deployment**: -`mökv` uses `gRPC` for efficient communication between clients and the cluster. - -- API Definition: The core `gRPC` service, `KV`, is defined in [`internal/api/kv.proto`](internal/api/kv.proto), exposing methods like `Get`, `Set`, `Delete`, `List`, and `GetServers`. +```bash +make docker-build +make kind-load +kubectl rollout restart statefulset mokv +``` -- `gRPC` Server: The server implementation resides in [`internal/server/server.go`](internal/server/server.go), handling `gRPC` requests. +**Scale the cluster**: -- Interceptors: `gRPC` Interceptors are used to handle: +```bash +helm upgrade mokv deploy/mokv --set replicas=5 +``` - - Logging: Each incoming request is logged for monitoring. +**Uninstall**: -- Client-Side Load Balancing (Name Resolution and Picker): `mökv` uses client-side load balancing. +```bash +make clean +``` - - Name Resolver ([`internal/discovery/resolver.go`](internal/discovery/resolver.go)): The name resolver periodically calls `GetServers` to discover available `mökv` nodes and their roles (Leader/Follower). It updates the list of available servers with the `is_leader` attribute. - - Picker ([`internal/discovery/picker.go`](internal/discovery/picker.go)): The Picker directs requests based on the operation type and the leader status of available connections: +## Local Development - - Writes (`Set`, `Delete`): These are routed to the _Leader_ node to ensure consistency. - - Reads (`Get`, `List`): These are balanced among available _Follower_ nodes for improved read performance. +For local testing without Kubernetes, see [`example/start_nodes.sh`](example/start_nodes.sh). diff --git a/api/health.pb.go b/api/health.pb.go index 0e24a2d..e8ab94f 100644 --- a/api/health.pb.go +++ b/api/health.pb.go @@ -116,7 +116,7 @@ func (x *HealthCheckRequest) GetService() string { type HealthCheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"` + Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=api.HealthCheckResponse_ServingStatus" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -162,18 +162,18 @@ var File_api_health_proto protoreflect.FileDescriptor const file_api_health_proto_rawDesc = "" + "\n" + - "\x10api/health.proto\x12\x0egrpc.health.v1\".\n" + + "\x10api/health.proto\x12\x03api\".\n" + "\x12HealthCheckRequest\x12\x18\n" + - "\aservice\x18\x01 \x01(\tR\aservice\"\x9c\x01\n" + - "\x13HealthCheckResponse\x12I\n" + - "\x06status\x18\x01 \x01(\x0e21.grpc.health.v1.HealthCheckResponse.ServingStatusR\x06status\":\n" + + "\aservice\x18\x01 \x01(\tR\aservice\"\x91\x01\n" + + "\x13HealthCheckResponse\x12>\n" + + "\x06status\x18\x01 \x01(\x0e2&.api.HealthCheckResponse.ServingStatusR\x06status\":\n" + "\rServingStatus\x12\v\n" + "\aUNKNOWN\x10\x00\x12\v\n" + "\aSERVING\x10\x01\x12\x0f\n" + - "\vNOT_SERVING\x10\x022\xae\x01\n" + - "\x06Health\x12P\n" + - "\x05Check\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse\x12R\n" + - "\x05Watch\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse0\x01B\"Z github.com/dynamic-calm/mokv/apib\x06proto3" + "\vNOT_SERVING\x10\x022\x82\x01\n" + + "\x06Health\x12:\n" + + "\x05Check\x12\x17.api.HealthCheckRequest\x1a\x18.api.HealthCheckResponse\x12<\n" + + "\x05Watch\x12\x17.api.HealthCheckRequest\x1a\x18.api.HealthCheckResponse0\x01B\"Z github.com/dynamic-calm/mokv/apib\x06proto3" var ( file_api_health_proto_rawDescOnce sync.Once @@ -190,16 +190,16 @@ func file_api_health_proto_rawDescGZIP() []byte { var file_api_health_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_api_health_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_api_health_proto_goTypes = []any{ - (HealthCheckResponse_ServingStatus)(0), // 0: grpc.health.v1.HealthCheckResponse.ServingStatus - (*HealthCheckRequest)(nil), // 1: grpc.health.v1.HealthCheckRequest - (*HealthCheckResponse)(nil), // 2: grpc.health.v1.HealthCheckResponse + (HealthCheckResponse_ServingStatus)(0), // 0: api.HealthCheckResponse.ServingStatus + (*HealthCheckRequest)(nil), // 1: api.HealthCheckRequest + (*HealthCheckResponse)(nil), // 2: api.HealthCheckResponse } var file_api_health_proto_depIdxs = []int32{ - 0, // 0: grpc.health.v1.HealthCheckResponse.status:type_name -> grpc.health.v1.HealthCheckResponse.ServingStatus - 1, // 1: grpc.health.v1.Health.Check:input_type -> grpc.health.v1.HealthCheckRequest - 1, // 2: grpc.health.v1.Health.Watch:input_type -> grpc.health.v1.HealthCheckRequest - 2, // 3: grpc.health.v1.Health.Check:output_type -> grpc.health.v1.HealthCheckResponse - 2, // 4: grpc.health.v1.Health.Watch:output_type -> grpc.health.v1.HealthCheckResponse + 0, // 0: api.HealthCheckResponse.status:type_name -> api.HealthCheckResponse.ServingStatus + 1, // 1: api.Health.Check:input_type -> api.HealthCheckRequest + 1, // 2: api.Health.Watch:input_type -> api.HealthCheckRequest + 2, // 3: api.Health.Check:output_type -> api.HealthCheckResponse + 2, // 4: api.Health.Watch:output_type -> api.HealthCheckResponse 3, // [3:5] is the sub-list for method output_type 1, // [1:3] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name diff --git a/api/health.proto b/api/health.proto index 06d1f53..152a2d2 100644 --- a/api/health.proto +++ b/api/health.proto @@ -2,7 +2,7 @@ syntax = "proto3" ; option go_package = "github.com/dynamic-calm/mokv/api"; -package grpc.health.v1; +package api; message HealthCheckRequest { string service = 1; diff --git a/api/health_grpc.pb.go b/api/health_grpc.pb.go index bc08242..df8f5d6 100644 --- a/api/health_grpc.pb.go +++ b/api/health_grpc.pb.go @@ -19,8 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Health_Check_FullMethodName = "/grpc.health.v1.Health/Check" - Health_Watch_FullMethodName = "/grpc.health.v1.Health/Watch" + Health_Check_FullMethodName = "/api.Health/Check" + Health_Watch_FullMethodName = "/api.Health/Watch" ) // HealthClient is the client API for Health service. @@ -144,7 +144,7 @@ type Health_WatchServer = grpc.ServerStreamingServer[HealthCheckResponse] // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Health_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "grpc.health.v1.Health", + ServiceName: "api.Health", HandlerType: (*HealthServer)(nil), Methods: []grpc.MethodDesc{ { diff --git a/cmd/test_kv.go b/cmd/test_kv.go new file mode 100644 index 0000000..c7560e6 --- /dev/null +++ b/cmd/test_kv.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + + "github.com/dynamic-calm/mokv/api" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/emptypb" +) + +func main() { + addr := flag.String("addr", "localhost:9400", "service address") + flag.Parse() + + cc, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatal(err) + } + defer cc.Close() + + client := api.NewKVClient(cc) + ctx := context.Background() + + // Get Servers + fmt.Println("Getting servers:") + servers, err := client.GetServers(ctx, &emptypb.Empty{}) + if err != nil { + log.Fatal(err) + } + for _, s := range servers.Servers { + fmt.Printf("\t- %v -> is leader: %v\n", s, s.IsLeader) + } + + // Set + fmt.Println("Setting key 'hello' = 'world'") + setRes, err := client.Set(ctx, &api.SetRequest{Key: "hello", Value: []byte("world")}) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Set OK: %v\n\n", setRes.Ok) + + // Get + fmt.Println("Getting key 'hello'") + getRes, err := client.Get(ctx, &api.GetRequest{Key: "hello"}) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Got: %s = %s\n\n", getRes.Key, string(getRes.Value)) + +} diff --git a/deploy/mokv/templates/service.yaml b/deploy/mokv/templates/service.yaml new file mode 100644 index 0000000..e6a2adb --- /dev/null +++ b/deploy/mokv/templates/service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mokv.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{ include "mokv.labels" . | nindent 4 }} +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: rpc + port: {{ .Values.rpcPort }} + targetPort: {{ .Values.rpcPort }} + - name: serf-tcp + protocol: "TCP" + port: {{ .Values.serfPort }} + targetPort: {{ .Values.serfPort }} + - name: serf-udp + protocol: "UDP" + port: {{ .Values.serfPort }} + targetPort: {{ .Values.serfPort }} + selector: {{ include "mokv.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/deploy/mokv/templates/statefulset.yaml b/deploy/mokv/templates/statefulset.yaml index b4c6fc2..fffbabc 100644 --- a/deploy/mokv/templates/statefulset.yaml +++ b/deploy/mokv/templates/statefulset.yaml @@ -1,4 +1,3 @@ -# START: initial apiVersion: apps/v1 kind: StatefulSet metadata: @@ -26,21 +25,18 @@ spec: ID=$(echo $HOSTNAME | rev | cut -d- -f1 | rev) cat > /var/run/mokv/config.yaml < Date: Tue, 18 Nov 2025 07:52:23 +0100 Subject: [PATCH 3/4] fix: test connection --- Makefile | 2 +- mokv/mokv_test.go | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7ba102e..daa8df1 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ compile: .PHONY: test test: - go test -cover -race ./... + go test -cover -race -v ./... .PHONY: build build: diff --git a/mokv/mokv_test.go b/mokv/mokv_test.go index 5f60bd5..11cb268 100644 --- a/mokv/mokv_test.go +++ b/mokv/mokv_test.go @@ -2,8 +2,6 @@ package mokv_test import ( "bytes" - "context" - "fmt" "net/http" "os" "strconv" @@ -18,7 +16,7 @@ import ( ) func TestRunE2E(t *testing.T) { - ctx := context.Background() + ctx := t.Context() // Setup test data directory testDir := t.TempDir() @@ -39,19 +37,19 @@ func TestRunE2E(t *testing.T) { m, err := mokv.New(cfg, os.Getenv) if err != nil { - t.Fatalf("failed to creating new mokv: %s", err) + t.Fatalf("failed to create new mokv: %s", err) } go func() { m.Listen(ctx) }() - time.Sleep(3 * time.Second) + time.Sleep(2 * time.Second) // Setup client connection rpcAddr := "127.0.0.1:" + strconv.Itoa(cfg.RPCPort) conn, err := grpc.NewClient( - fmt.Sprintf("mokv://%s", rpcAddr), + rpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { From 5a14997ed873558525d9d8ec0a8fcc724fad1fe3 Mon Sep 17 00:00:00 2001 From: Mateo Presa Castro Date: Tue, 18 Nov 2025 07:55:32 +0100 Subject: [PATCH 4/4] chore: remove comments --- kv/kv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kv/kv.go b/kv/kv.go index 886aeac..268b57d 100644 --- a/kv/kv.go +++ b/kv/kv.go @@ -275,8 +275,8 @@ func (kv *KV) setupRaft(dataDir string) error { config.LocalID = kv.cfg.Raft.LocalID config.HeartbeatTimeout = 1 * time.Second config.ElectionTimeout = 3 * time.Second - config.LeaderLeaseTimeout = 500 * time.Millisecond // 500ms instead of 25ms - config.CommitTimeout = 500 * time.Millisecond // 500ms instead of 50ms + config.LeaderLeaseTimeout = 500 * time.Millisecond + config.CommitTimeout = 500 * time.Millisecond config.SnapshotInterval = 120 * time.Second config.SnapshotThreshold = 8192 config.MaxAppendEntries = 64