Fix meta.Client CreateDatabaseWithRetentionPolicy RPC command

Previously, meta.Client would drop the default retention policy when
trying to create a database with a retention policy. The RPC has now
been modified to include the desired retention policy in the
CreateDatabase command and have it use that retention policy information
instead of the default configuration when provided.

This also lowers the number of RPC calls for
CreateDatabaseWithRetentionPolicy to only a single RPC call instead of
two.

Protections have also been included so creating a retention policy with
different parameters will return an error similar to if you tried to
modify the retention policy separately.

Fixes #5696.
pull/5705/head
Jonathan A. Sternberg 2016-02-16 12:03:43 -05:00
parent 94a383e66d
commit 23da067593
7 changed files with 73 additions and 37 deletions

View File

@ -28,6 +28,7 @@
- [#5695](https://github.com/influxdata/influxdb/pull/5695): Remove meta servers from node.json
- [#5606](https://github.com/influxdata/influxdb/issues/5606): TSM conversion reproducibly drops data silently
- [#5656](https://github.com/influxdata/influxdb/issues/5656): influx\_tsm: panic during conversion
- [#5696](https://github.com/influxdata/influxdb/issues/5696): Do not drop the database when creating with a retention policy
## v0.10.0 [2016-02-04]

View File

@ -64,9 +64,9 @@ func init() {
exp: `{"results":[{}]}`,
},
&Query{
name: "create database with retention duration should not error with existing database with IF NOT EXISTS",
name: "create database with retention duration should error if retention policy is different with IF NOT EXISTS",
command: `CREATE DATABASE IF NOT EXISTS db1 WITH DURATION 24h`,
exp: `{"results":[{}]}`,
exp: `{"results":[{"error":"retention policy conflicts with an existing policy"}]}`,
},
&Query{
name: "create database should error IF NOT EXISTS with bad retention duration",

View File

@ -343,24 +343,24 @@ func (c *Client) CreateDatabaseWithRetentionPolicy(name string, rpi *RetentionPo
return nil, ErrRetentionPolicyDurationTooLow
}
if _, err := c.CreateDatabase(name); err != nil {
return nil, err
if db, _ := c.Database(name); db != nil {
// Check if the retention policy already exists. If it does and matches
// the desired retention policy, exit with no error.
if rp := db.RetentionPolicy(rpi.Name); rp != nil {
if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration {
return nil, ErrRetentionPolicyConflict
}
return db, nil
}
}
if err := c.DropRetentionPolicy(name, rpi.Name); err != nil {
return nil, err
}
cmd := &internal.CreateRetentionPolicyCommand{
Database: proto.String(name),
cmd := &internal.CreateDatabaseCommand{
Name: proto.String(name),
RetentionPolicy: rpi.marshal(),
}
if err := c.retryUntilExec(internal.Command_CreateRetentionPolicyCommand, internal.E_CreateRetentionPolicyCommand_Command, cmd); err != nil {
return nil, err
}
if err := c.SetDefaultRetentionPolicy(name, rpi.Name); err != nil {
err := c.retryUntilExec(internal.Command_CreateDatabaseCommand, internal.E_CreateDatabaseCommand_Command, cmd)
if err != nil {
return nil, err
}

View File

@ -66,6 +66,10 @@ var (
ErrRetentionPolicyDurationTooLow = errors.New(fmt.Sprintf("retention policy duration must be at least %s",
MinRetentionPolicyDuration))
// ErrRetentionPolicyConflict is returned when creating a retention policy conflicts
// with an existing policy.
ErrRetentionPolicyConflict = errors.New("retention policy conflicts with an existing policy")
// ErrReplicationFactorTooLow is returned when the replication factor is not in an
// acceptable range.
ErrReplicationFactorTooLow = errors.New("replication factor must be greater than 0")

View File

@ -183,6 +183,7 @@ type Data struct {
MaxNodeID *uint64 `protobuf:"varint,7,req,name=MaxNodeID" json:"MaxNodeID,omitempty"`
MaxShardGroupID *uint64 `protobuf:"varint,8,req,name=MaxShardGroupID" json:"MaxShardGroupID,omitempty"`
MaxShardID *uint64 `protobuf:"varint,9,req,name=MaxShardID" json:"MaxShardID,omitempty"`
// added for 0.10.0
DataNodes []*NodeInfo `protobuf:"bytes,10,rep,name=DataNodes" json:"DataNodes,omitempty"`
MetaNodes []*NodeInfo `protobuf:"bytes,11,rep,name=MetaNodes" json:"MetaNodes,omitempty"`
XXX_unrecognized []byte `json:"-"`
@ -644,6 +645,8 @@ func (m *Command) GetType() Command_Type {
return Command_CreateNodeCommand
}
// This isn't used in >= 0.10.0. Kept around for upgrade purposes. Instead
// look at CreateDataNodeCommand and CreateMetaNodeCommand
type CreateNodeCommand struct {
Host *string `protobuf:"bytes,1,req,name=Host" json:"Host,omitempty"`
Rand *uint64 `protobuf:"varint,2,req,name=Rand" json:"Rand,omitempty"`
@ -710,6 +713,7 @@ var E_DeleteNodeCommand_Command = &proto.ExtensionDesc{
type CreateDatabaseCommand struct {
Name *string `protobuf:"bytes,1,req,name=Name" json:"Name,omitempty"`
RetentionPolicy *RetentionPolicyInfo `protobuf:"bytes,2,opt,name=RetentionPolicy" json:"RetentionPolicy,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -724,6 +728,13 @@ func (m *CreateDatabaseCommand) GetName() string {
return ""
}
func (m *CreateDatabaseCommand) GetRetentionPolicy() *RetentionPolicyInfo {
if m != nil {
return m.RetentionPolicy
}
return nil
}
var E_CreateDatabaseCommand_Command = &proto.ExtensionDesc{
ExtendedType: (*Command)(nil),
ExtensionType: (*CreateDatabaseCommand)(nil),
@ -1604,6 +1615,8 @@ func (m *Response) GetIndex() uint64 {
return 0
}
// SetMetaNodeCommand is for the initial metanode in a cluster or
// if the single host restarts and its hostname changes, this will update it
type SetMetaNodeCommand struct {
HTTPAddr *string `protobuf:"bytes,1,req,name=HTTPAddr" json:"HTTPAddr,omitempty"`
TCPAddr *string `protobuf:"bytes,2,req,name=TCPAddr" json:"TCPAddr,omitempty"`

View File

@ -154,6 +154,7 @@ message CreateDatabaseCommand {
optional CreateDatabaseCommand command = 103;
}
required string Name = 1;
optional RetentionPolicyInfo RetentionPolicy = 2;
}
message DropDatabaseCommand {

View File

@ -208,7 +208,24 @@ func (fsm *storeFSM) applyCreateDatabaseCommand(cmd *internal.Command) interface
}
s := (*store)(fsm)
if s.config.RetentionAutoCreate {
if rpi := v.GetRetentionPolicy(); rpi != nil {
if err := other.CreateRetentionPolicy(v.GetName(), &RetentionPolicyInfo{
Name: rpi.GetName(),
ReplicaN: int(rpi.GetReplicaN()),
Duration: time.Duration(rpi.GetDuration()),
ShardGroupDuration: time.Duration(rpi.GetShardGroupDuration()),
}); err != nil {
if err == ErrRetentionPolicyExists {
return ErrRetentionPolicyConflict
}
return err
}
// Set it as the default retention policy.
if err := other.SetDefaultRetentionPolicy(v.GetName(), rpi.GetName()); err != nil {
return err
}
} else if s.config.RetentionAutoCreate {
// Read node count.
// Retention policies must be fully replicated.
replicaN := len(other.DataNodes)