diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b66343..62291707 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ - [PR #738](https://github.com/rqlite/rqlite/pull/738): Switch to rqlite fork of mattn/go-sqlite3. The SQLite C code remains unchanged. - [PR #741](https://github.com/rqlite/rqlite/pull/741): Tighten up Store-level locking. - [PR #747](https://github.com/rqlite/rqlite/pull/747): Time snapshot, restore, and startup times. -- [PR #750](https://github.com/rqlite/rqlite/pull/750): Build on-disk databases in-memory first. Fixes [issue #731](https://github.com/rqlite/rqlite/issues/731). +- [PR #750](https://github.com/rqlite/rqlite/pull/750): Build on-disk databases in-memory first. Fixes [issue #731](https://github.com/rqlite/rqlite/ +issues/731). +- [PR #754](https://github.com/rqlite/rqlite/pull/754): Support Noop commands in Raft Log. ## 5.9.0 (January 24th 2021) ### New features diff --git a/command/command.pb.go b/command/command.pb.go index 805bd5ec..ab050212 100644 --- a/command/command.pb.go +++ b/command/command.pb.go @@ -82,6 +82,7 @@ const ( Command_COMMAND_TYPE_EXECUTE Command_Type = 2 Command_COMMAND_TYPE_METADATA_SET Command_Type = 3 Command_COMMAND_TYPE_METADATA_DELETE Command_Type = 4 + Command_COMMAND_TYPE_NOOP Command_Type = 5 ) // Enum value maps for Command_Type. @@ -92,6 +93,7 @@ var ( 2: "COMMAND_TYPE_EXECUTE", 3: "COMMAND_TYPE_METADATA_SET", 4: "COMMAND_TYPE_METADATA_DELETE", + 5: "COMMAND_TYPE_NOOP", } Command_Type_value = map[string]int32{ "COMMAND_TYPE_UNKNOWN": 0, @@ -99,6 +101,7 @@ var ( "COMMAND_TYPE_EXECUTE": 2, "COMMAND_TYPE_METADATA_SET": 3, "COMMAND_TYPE_METADATA_DELETE": 4, + "COMMAND_TYPE_NOOP": 5, } ) @@ -126,7 +129,7 @@ func (x Command_Type) Number() protoreflect.EnumNumber { // Deprecated: Use Command_Type.Descriptor instead. func (Command_Type) EnumDescriptor() ([]byte, []int) { - return file_command_proto_rawDescGZIP(), []int{7, 0} + return file_command_proto_rawDescGZIP(), []int{8, 0} } type Parameter struct { @@ -589,6 +592,53 @@ func (x *MetadataDelete) GetRaftId() string { return "" } +type Noop struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *Noop) Reset() { + *x = Noop{} + if protoimpl.UnsafeEnabled { + mi := &file_command_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Noop) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Noop) ProtoMessage() {} + +func (x *Noop) ProtoReflect() protoreflect.Message { + mi := &file_command_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Noop.ProtoReflect.Descriptor instead. +func (*Noop) Descriptor() ([]byte, []int) { + return file_command_proto_rawDescGZIP(), []int{7} +} + +func (x *Noop) GetId() string { + if x != nil { + return x.Id + } + return "" +} + type Command struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -602,7 +652,7 @@ type Command struct { func (x *Command) Reset() { *x = Command{} if protoimpl.UnsafeEnabled { - mi := &file_command_proto_msgTypes[7] + mi := &file_command_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -615,7 +665,7 @@ func (x *Command) String() string { func (*Command) ProtoMessage() {} func (x *Command) ProtoReflect() protoreflect.Message { - mi := &file_command_proto_msgTypes[7] + mi := &file_command_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -628,7 +678,7 @@ func (x *Command) ProtoReflect() protoreflect.Message { // Deprecated: Use Command.ProtoReflect.Descriptor instead. func (*Command) Descriptor() ([]byte, []int) { - return file_command_proto_rawDescGZIP(), []int{7} + return file_command_proto_rawDescGZIP(), []int{8} } func (x *Command) GetType() Command_Type { @@ -708,27 +758,30 @@ var file_command_proto_rawDesc = []byte{ 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, 0x0a, 0x0e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x61, 0x66, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x61, 0x66, 0x74, 0x49, 0x64, 0x22, 0x8b, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, - 0x22, 0x93, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, - 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x59, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, - 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x45, 0x43, - 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x53, - 0x45, 0x54, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x44, 0x45, - 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x71, 0x6c, 0x69, 0x74, 0x65, 0x2f, 0x72, 0x71, 0x6c, 0x69, - 0x74, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x06, 0x72, 0x61, 0x66, 0x74, 0x49, 0x64, 0x22, 0x16, 0x0a, 0x04, 0x4e, 0x6f, 0x6f, 0x70, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0xa2, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x75, + 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x43, + 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x51, 0x55, 0x45, 0x52, + 0x59, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x1d, 0x0a, + 0x19, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, + 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, + 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, + 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x15, + 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, + 0x4f, 0x4f, 0x50, 0x10, 0x05, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x71, 0x6c, 0x69, 0x74, 0x65, 0x2f, 0x72, 0x71, 0x6c, 0x69, 0x74, + 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -744,7 +797,7 @@ func file_command_proto_rawDescGZIP() []byte { } var file_command_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_command_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_command_proto_goTypes = []interface{}{ (QueryRequest_Level)(0), // 0: command.QueryRequest.Level (Command_Type)(0), // 1: command.Command.Type @@ -755,8 +808,9 @@ var file_command_proto_goTypes = []interface{}{ (*ExecuteRequest)(nil), // 6: command.ExecuteRequest (*MetadataSet)(nil), // 7: command.MetadataSet (*MetadataDelete)(nil), // 8: command.MetadataDelete - (*Command)(nil), // 9: command.Command - nil, // 10: command.MetadataSet.DataEntry + (*Noop)(nil), // 9: command.Noop + (*Command)(nil), // 10: command.Command + nil, // 11: command.MetadataSet.DataEntry } var file_command_proto_depIdxs = []int32{ 2, // 0: command.Statement.parameters:type_name -> command.Parameter @@ -764,7 +818,7 @@ var file_command_proto_depIdxs = []int32{ 4, // 2: command.QueryRequest.request:type_name -> command.Request 0, // 3: command.QueryRequest.level:type_name -> command.QueryRequest.Level 4, // 4: command.ExecuteRequest.request:type_name -> command.Request - 10, // 5: command.MetadataSet.data:type_name -> command.MetadataSet.DataEntry + 11, // 5: command.MetadataSet.data:type_name -> command.MetadataSet.DataEntry 1, // 6: command.Command.type:type_name -> command.Command.Type 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type @@ -864,6 +918,18 @@ func file_command_proto_init() { } } file_command_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Noop); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_command_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Command); i { case 0: return &v.state @@ -889,7 +955,7 @@ func file_command_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_command_proto_rawDesc, NumEnums: 2, - NumMessages: 9, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/command/command.proto b/command/command.proto index 1f707470..40504c41 100644 --- a/command/command.proto +++ b/command/command.proto @@ -49,6 +49,10 @@ message MetadataDelete { string raft_id = 1; } +message Noop { + string id = 1; +} + message Command { enum Type { COMMAND_TYPE_UNKNOWN = 0; @@ -56,6 +60,7 @@ message Command { COMMAND_TYPE_EXECUTE = 2; COMMAND_TYPE_METADATA_SET = 3; COMMAND_TYPE_METADATA_DELETE = 4; + COMMAND_TYPE_NOOP = 5; } Type type = 1; bytes sub_command = 2; diff --git a/command/marshal.go b/command/marshal.go index 586b5839..d6c3678a 100644 --- a/command/marshal.go +++ b/command/marshal.go @@ -165,6 +165,16 @@ func UnMarshalMetadataDelete(b []byte, c *MetadataDelete) error { return proto.Unmarshal(b, c) } +// MarshalNoop marshals a Noop command +func MarshalNoop(c *Noop) ([]byte, error) { + return proto.Marshal(c) +} + +// UnmarshalNoop unmarshals a Noop command +func UnMarshalNoop(b []byte, c *Noop) error { + return proto.Unmarshal(b, c) +} + // UnmarshalSubCommand unmarshalls a sub command m. It assumes that // m is the correct type. func UnmarshalSubCommand(c *Command, m proto.Message) error { diff --git a/store/store.go b/store/store.go index e17da26f..bd787f3e 100644 --- a/store/store.go +++ b/store/store.go @@ -743,6 +743,37 @@ func (s *Store) SetMetadata(md map[string]string) error { return s.setMetadata(s.raftID, md) } +// Noop writes a noop command to the Raft log. A noop command simply +// consumes a slot in the Raft log, but has no other affect on the +// system. +func (s *Store) Noop(id string) error { + n := &command.Noop{ + Id: id, + } + b, err := command.MarshalNoop(n) + if err != nil { + return err + } + + c := &command.Command{ + Type: command.Command_COMMAND_TYPE_NOOP, + SubCommand: b, + } + bc, err := command.Marshal(c) + if err != nil { + return err + } + + f := s.raft.Apply(bc, s.ApplyTimeout) + if e := f.(raft.Future); e.Error() != nil { + if e.Error() == raft.ErrNotLeader { + return ErrNotLeader + } + return e.Error() + } + return nil +} + // setMetadata adds the metadata md to any existing metadata for // the given node ID. func (s *Store) setMetadata(id string, md map[string]string) error { @@ -1022,6 +1053,8 @@ func (s *Store) Apply(l *raft.Log) (e interface{}) { delete(s.meta, md.RaftId) }() return &fsmGenericResponse{} + case command.Command_COMMAND_TYPE_NOOP: + return &fsmGenericResponse{} default: return &fsmGenericResponse{error: fmt.Errorf("unhandled command: %v", c.Type)} } diff --git a/store/store_test.go b/store/store_test.go index 79c0f5d6..c4e8a49a 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -1155,6 +1155,19 @@ func Test_SingleNodeRestoreNoncompressed(t *testing.T) { } } +func Test_SingleNodeNoop(t *testing.T) { + s0 := mustNewStore(true) + if err := s0.Open(true); err != nil { + t.Fatalf("failed to open single-node store: %s", err.Error()) + } + defer s0.Close(true) + s0.WaitForLeader(10 * time.Second) + + if err := s0.Noop("1"); err != nil { + t.Fatalf("failed to write noop command: %s", err.Error()) + } +} + func Test_MetadataMultinode(t *testing.T) { s0 := mustNewStore(true) if err := s0.Open(true); err != nil {