Skip to content

Commit b7ccc51

Browse files
Lars Maierneunhoef
Lars Maier
authored andcommitted
Implemented MinReplicationFactor. (#196)
1 parent 4cd2eb4 commit b7ccc51

7 files changed

+201
-31
lines changed

cluster.go

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ type InventoryCollectionParameters struct {
203203
Path string `json:"path,omitempty"`
204204
PlanID string `json:"planId,omitempty"`
205205
ReplicationFactor int `json:"replicationFactor,omitempty"`
206+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
206207
ShardKeys []string `json:"shardKeys,omitempty"`
207208
Shards map[ShardID][]ServerID `json:"shards,omitempty"`
208209
Status CollectionStatus `json:"status,omitempty"`

cluster_impl.go

+3
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ type inventoryCollectionParametersInternal struct {
249249
Path string `json:"path,omitempty"`
250250
PlanID string `json:"planId,omitempty"`
251251
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
252+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
252253
ShardKeys []string `json:"shardKeys,omitempty"`
253254
Shards map[ShardID][]ServerID `json:"shards,omitempty"`
254255
Status CollectionStatus `json:"status,omitempty"`
@@ -277,6 +278,7 @@ func (p *InventoryCollectionParameters) asInternal() inventoryCollectionParamete
277278
Path: p.Path,
278279
PlanID: p.PlanID,
279280
ReplicationFactor: replicationFactor(p.ReplicationFactor),
281+
MinReplicationFactor: p.MinReplicationFactor,
280282
ShardKeys: p.ShardKeys,
281283
Shards: p.Shards,
282284
Status: p.Status,
@@ -310,6 +312,7 @@ func (p *inventoryCollectionParametersInternal) asExternal() InventoryCollection
310312
Path: p.Path,
311313
PlanID: p.PlanID,
312314
ReplicationFactor: int(p.ReplicationFactor),
315+
MinReplicationFactor: p.MinReplicationFactor,
313316
ShardKeys: p.ShardKeys,
314317
Shards: p.Shards,
315318
Status: p.Status,

collection.go

+6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ type CollectionProperties struct {
116116
// ReplicationFactor contains how many copies of each shard are kept on different DBServers.
117117
// Only available in cluster setup.
118118
ReplicationFactor int `json:"replicationFactor,omitempty"`
119+
// MinReplicationFactor contains how many copies must be available before a collection can be written.
120+
// It is required that 1 <= MinReplicationFactor <= ReplicationFactor.
121+
// Default is 1. Not available for satellite collections.
122+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
119123
// SmartJoinAttribute
120124
// See documentation for smart joins.
121125
// This requires ArangoDB Enterprise Edition.
@@ -144,6 +148,8 @@ type SetCollectionPropertiesOptions struct {
144148
// ReplicationFactor contains how many copies of each shard are kept on different DBServers.
145149
// Only available in cluster setup.
146150
ReplicationFactor int `json:"replicationFactor,omitempty"`
151+
// MinReplicationFactor contains how many copies must be available before a collection can be written.
152+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
147153
}
148154

149155
// CollectionStatus indicates the status of a collection.

collection_impl.go

+38-31
Original file line numberDiff line numberDiff line change
@@ -274,40 +274,43 @@ type collectionPropertiesInternal struct {
274274
Type KeyGeneratorType `json:"type,omitempty"`
275275
AllowUserKeys bool `json:"allowUserKeys,omitempty"`
276276
} `json:"keyOptions,omitempty"`
277-
NumberOfShards int `json:"numberOfShards,omitempty"`
278-
ShardKeys []string `json:"shardKeys,omitempty"`
279-
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
280-
SmartJoinAttribute string `json:"smartJoinAttribute,omitempty"`
281-
ShardingStrategy ShardingStrategy `json:"shardingStrategy,omitempty"`
277+
NumberOfShards int `json:"numberOfShards,omitempty"`
278+
ShardKeys []string `json:"shardKeys,omitempty"`
279+
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
280+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
281+
SmartJoinAttribute string `json:"smartJoinAttribute,omitempty"`
282+
ShardingStrategy ShardingStrategy `json:"shardingStrategy,omitempty"`
282283
}
283284

284285
func (p *collectionPropertiesInternal) asExternal() CollectionProperties {
285286
return CollectionProperties{
286-
CollectionInfo: p.CollectionInfo,
287-
WaitForSync: p.WaitForSync,
288-
DoCompact: p.DoCompact,
289-
JournalSize: p.JournalSize,
290-
KeyOptions: p.KeyOptions,
291-
NumberOfShards: p.NumberOfShards,
292-
ShardKeys: p.ShardKeys,
293-
ReplicationFactor: int(p.ReplicationFactor),
294-
SmartJoinAttribute: p.SmartJoinAttribute,
295-
ShardingStrategy: p.ShardingStrategy,
287+
CollectionInfo: p.CollectionInfo,
288+
WaitForSync: p.WaitForSync,
289+
DoCompact: p.DoCompact,
290+
JournalSize: p.JournalSize,
291+
KeyOptions: p.KeyOptions,
292+
NumberOfShards: p.NumberOfShards,
293+
ShardKeys: p.ShardKeys,
294+
ReplicationFactor: int(p.ReplicationFactor),
295+
MinReplicationFactor: p.MinReplicationFactor,
296+
SmartJoinAttribute: p.SmartJoinAttribute,
297+
ShardingStrategy: p.ShardingStrategy,
296298
}
297299
}
298300

299301
func (p *CollectionProperties) asInternal() collectionPropertiesInternal {
300302
return collectionPropertiesInternal{
301-
CollectionInfo: p.CollectionInfo,
302-
WaitForSync: p.WaitForSync,
303-
DoCompact: p.DoCompact,
304-
JournalSize: p.JournalSize,
305-
KeyOptions: p.KeyOptions,
306-
NumberOfShards: p.NumberOfShards,
307-
ShardKeys: p.ShardKeys,
308-
ReplicationFactor: replicationFactor(p.ReplicationFactor),
309-
SmartJoinAttribute: p.SmartJoinAttribute,
310-
ShardingStrategy: p.ShardingStrategy,
303+
CollectionInfo: p.CollectionInfo,
304+
WaitForSync: p.WaitForSync,
305+
DoCompact: p.DoCompact,
306+
JournalSize: p.JournalSize,
307+
KeyOptions: p.KeyOptions,
308+
NumberOfShards: p.NumberOfShards,
309+
ShardKeys: p.ShardKeys,
310+
ReplicationFactor: replicationFactor(p.ReplicationFactor),
311+
MinReplicationFactor: p.MinReplicationFactor,
312+
SmartJoinAttribute: p.SmartJoinAttribute,
313+
ShardingStrategy: p.ShardingStrategy,
311314
}
312315
}
313316

@@ -320,6 +323,7 @@ func (p *CollectionProperties) fromInternal(i *collectionPropertiesInternal) {
320323
p.NumberOfShards = i.NumberOfShards
321324
p.ShardKeys = i.ShardKeys
322325
p.ReplicationFactor = int(i.ReplicationFactor)
326+
p.MinReplicationFactor = i.MinReplicationFactor
323327
p.SmartJoinAttribute = i.SmartJoinAttribute
324328
p.ShardingStrategy = i.ShardingStrategy
325329
}
@@ -341,23 +345,26 @@ func (p *CollectionProperties) UnmarshalJSON(d []byte) error {
341345
}
342346

343347
type setCollectionPropertiesOptionsInternal struct {
344-
WaitForSync *bool `json:"waitForSync,omitempty"`
345-
JournalSize int64 `json:"journalSize,omitempty"`
346-
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
348+
WaitForSync *bool `json:"waitForSync,omitempty"`
349+
JournalSize int64 `json:"journalSize,omitempty"`
350+
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
351+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
347352
}
348353

349354
func (p *SetCollectionPropertiesOptions) asInternal() setCollectionPropertiesOptionsInternal {
350355
return setCollectionPropertiesOptionsInternal{
351-
WaitForSync: p.WaitForSync,
352-
JournalSize: p.JournalSize,
353-
ReplicationFactor: replicationFactor(p.ReplicationFactor),
356+
WaitForSync: p.WaitForSync,
357+
JournalSize: p.JournalSize,
358+
ReplicationFactor: replicationFactor(p.ReplicationFactor),
359+
MinReplicationFactor: p.MinReplicationFactor,
354360
}
355361
}
356362

357363
func (p *SetCollectionPropertiesOptions) fromInternal(i *setCollectionPropertiesOptionsInternal) {
358364
p.WaitForSync = i.WaitForSync
359365
p.JournalSize = i.JournalSize
360366
p.ReplicationFactor = int(i.ReplicationFactor)
367+
p.MinReplicationFactor = i.MinReplicationFactor
361368
}
362369

363370
// MarshalJSON converts SetCollectionPropertiesOptions into json

database_collections.go

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ type CreateCollectionOptions struct {
5252
// before the write operation is reported successful. If a server fails, this is detected automatically
5353
// and one of the servers holding copies take over, usually without an error being reported.
5454
ReplicationFactor int `json:"replicationFactor,omitempty"`
55+
// MinReplicationFactor contains how many copies must be available before a collection can be written.
56+
// It is required that 1 <= MinReplicationFactor <= ReplicationFactor.
57+
// Default is 1. Not available for satellite collections.
58+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
5559
// If true then the data is synchronized to disk before returning from a document create, update, replace or removal operation. (default: false)
5660
WaitForSync bool `json:"waitForSync,omitempty"`
5761
// Whether or not the collection will be compacted (default is true)

database_collections_impl.go

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func (d *database) Collections(ctx context.Context) ([]Collection, error) {
104104
type createCollectionOptionsInternal struct {
105105
JournalSize int `json:"journalSize,omitempty"`
106106
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
107+
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
107108
WaitForSync bool `json:"waitForSync,omitempty"`
108109
DoCompact *bool `json:"doCompact,omitempty"`
109110
IsVolatile bool `json:"isVolatile,omitempty"`
@@ -174,6 +175,7 @@ func (d *database) CreateCollection(ctx context.Context, name string, options *C
174175
func (p *createCollectionOptionsInternal) fromExternal(i *CreateCollectionOptions) {
175176
p.JournalSize = i.JournalSize
176177
p.ReplicationFactor = replicationFactor(i.ReplicationFactor)
178+
p.MinReplicationFactor = i.MinReplicationFactor
177179
p.WaitForSync = i.WaitForSync
178180
p.DoCompact = i.DoCompact
179181
p.IsVolatile = i.IsVolatile

test/collection_test.go

+147
Original file line numberDiff line numberDiff line change
@@ -552,3 +552,150 @@ func TestCollectionStatistics(t *testing.T) {
552552
}
553553
}
554554
}
555+
556+
// TestCollectionMinReplFactCreate creates a collection with minReplicationFactor != 1
557+
func TestCollectionMinReplFactCreate(t *testing.T) {
558+
c := createClientFromEnv(t, true)
559+
skipBelowVersion(c, "3.5", t)
560+
db := ensureDatabase(nil, c, "collection_min_repl_test", nil, t)
561+
name := "test_min_repl_create"
562+
minRepl := 2
563+
options := driver.CreateCollectionOptions{
564+
ReplicationFactor: minRepl,
565+
MinReplicationFactor: minRepl,
566+
}
567+
if _, err := db.CreateCollection(nil, name, &options); err != nil {
568+
t.Fatalf("Failed to create collection '%s': %s", name, describe(err))
569+
}
570+
571+
// Collection must exist now
572+
if found, err := db.CollectionExists(nil, name); err != nil {
573+
t.Errorf("CollectionExists('%s') failed: %s", name, describe(err))
574+
} else if !found {
575+
t.Errorf("CollectionExists('%s') return false, expected true", name)
576+
}
577+
// Check if the collection has a minReplicationFactor
578+
if col, err := db.Collection(nil, name); err != nil {
579+
t.Errorf("Collection('%s') failed: %s", name, describe(err))
580+
} else {
581+
if prop, err := col.Properties(nil); err != nil {
582+
t.Errorf("Properties() failed: %s", describe(err))
583+
} else {
584+
if prop.MinReplicationFactor != minRepl {
585+
t.Errorf("Collection does not have the correct min replication factor value, expected `%d`, found `%d`", minRepl, prop.MinReplicationFactor)
586+
}
587+
}
588+
}
589+
}
590+
591+
// TestCollectionMinReplFactInvalid creates a collection with minReplicationFactor > replicationFactor
592+
func TestCollectionMinReplFactInvalid(t *testing.T) {
593+
c := createClientFromEnv(t, true)
594+
skipBelowVersion(c, "3.5", t)
595+
skipNoCluster(c, t)
596+
db := ensureDatabase(nil, c, "collection_min_repl_test", nil, t)
597+
name := "test_min_repl_create_invalid"
598+
minRepl := 2
599+
options := driver.CreateCollectionOptions{
600+
ReplicationFactor: minRepl,
601+
MinReplicationFactor: minRepl + 1,
602+
}
603+
if _, err := db.CreateCollection(nil, name, &options); err == nil {
604+
t.Fatalf("CreateCollection('%s') did not fail", name)
605+
}
606+
// Collection must not exist now
607+
if found, err := db.CollectionExists(nil, name); err != nil {
608+
t.Errorf("CollectionExists('%s') failed: %s", name, describe(err))
609+
} else if found {
610+
t.Errorf("Collection %s should not exist", name)
611+
}
612+
}
613+
614+
// TestCollectionMinReplFactClusterInv tests if minReplicationFactor is forwarded to ClusterInfo
615+
func TestCollectionMinReplFactClusterInv(t *testing.T) {
616+
c := createClientFromEnv(t, true)
617+
skipBelowVersion(c, "3.5", t)
618+
skipNoCluster(c, t)
619+
db := ensureDatabase(nil, c, "collection_min_repl_test", nil, t)
620+
name := "test_min_repl_cluster_invent"
621+
minRepl := 2
622+
ensureCollection(nil, db, name, &driver.CreateCollectionOptions{
623+
ReplicationFactor: minRepl,
624+
MinReplicationFactor: minRepl,
625+
}, t)
626+
627+
cc, err := c.Cluster(nil)
628+
if err != nil {
629+
t.Fatalf("Failed to get Cluster: %s", describe(err))
630+
}
631+
632+
inv, err := cc.DatabaseInventory(nil, db)
633+
if err != nil {
634+
t.Fatalf("Failed to get Database Inventory: %s", describe(err))
635+
}
636+
637+
col, found := inv.CollectionByName(name)
638+
if !found {
639+
t.Fatalf("Failed to get find collection: %s", describe(err))
640+
}
641+
642+
if col.Parameters.MinReplicationFactor != minRepl {
643+
t.Errorf("Collection does not have the correct min replication factor value, expected `%d`, found `%d`", minRepl, col.Parameters.MinReplicationFactor)
644+
}
645+
}
646+
647+
// TestCollectionMinReplFactSetProp updates the minimal replication factor using SetProperties
648+
func TestCollectionMinReplFactSetProp(t *testing.T) {
649+
c := createClientFromEnv(t, true)
650+
skipBelowVersion(c, "3.5", t)
651+
skipNoCluster(c, t)
652+
db := ensureDatabase(nil, c, "collection_min_repl_test", nil, t)
653+
name := "test_min_repl_set_prop"
654+
minRepl := 2
655+
col := ensureCollection(nil, db, name, &driver.CreateCollectionOptions{
656+
ReplicationFactor: minRepl,
657+
MinReplicationFactor: minRepl,
658+
}, t)
659+
660+
if err := col.SetProperties(nil, driver.SetCollectionPropertiesOptions{
661+
MinReplicationFactor: 1,
662+
}); err != nil {
663+
t.Fatalf("Failed to update properties: %s", describe(err))
664+
}
665+
666+
if prop, err := col.Properties(nil); err != nil {
667+
t.Fatalf("Failed to get properties: %s", describe(err))
668+
} else {
669+
if prop.MinReplicationFactor != 1 {
670+
t.Fatalf("MinReplicationFactor not updated, expected %d, found %d", 1, prop.MinReplicationFactor)
671+
}
672+
}
673+
}
674+
675+
// TestCollectionMinReplFactSetPropInvalid updates the minimal replication factor to an invalid value using SetProperties
676+
func TestCollectionMinReplFactSetPropInvalid(t *testing.T) {
677+
c := createClientFromEnv(t, true)
678+
skipBelowVersion(c, "3.5", t)
679+
skipNoCluster(c, t)
680+
db := ensureDatabase(nil, c, "collection_min_repl_test", nil, t)
681+
name := "test_min_repl_set_prop_inv"
682+
minRepl := 2
683+
col := ensureCollection(nil, db, name, &driver.CreateCollectionOptions{
684+
ReplicationFactor: minRepl,
685+
MinReplicationFactor: minRepl,
686+
}, t)
687+
688+
if err := col.SetProperties(nil, driver.SetCollectionPropertiesOptions{
689+
MinReplicationFactor: minRepl + 1,
690+
}); err == nil {
691+
t.Errorf("SetProperties did not fail")
692+
}
693+
694+
if prop, err := col.Properties(nil); err != nil {
695+
t.Fatalf("Failed to get properties: %s", describe(err))
696+
} else {
697+
if prop.MinReplicationFactor != minRepl {
698+
t.Fatalf("MinReplicationFactor not updated, expected %d, found %d", minRepl, prop.MinReplicationFactor)
699+
}
700+
}
701+
}

0 commit comments

Comments
 (0)