Skip to content

Commit bee8f31

Browse files
author
Stanley Halka
committed
fix(diffDynamoDbGSI): ignore ordering of non_key_attributes in equality check to stop forced reconstruction of GSI [fixes ##3828]
1 parent be01d68 commit bee8f31

3 files changed

Lines changed: 87 additions & 3 deletions

File tree

aws/resource_aws_dynamodb_table.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func resourceAwsDynamoDbTable() *schema.Resource {
208208
Required: true,
209209
},
210210
"non_key_attributes": {
211-
Type: schema.TypeList,
211+
Type: schema.TypeSet,
212212
Optional: true,
213213
Elem: &schema.Schema{Type: schema.TypeString},
214214
},

aws/resource_aws_dynamodb_table_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestDiffDynamoDbGSI(t *testing.T) {
5858
New []interface{}
5959
ExpectedUpdates []*dynamodb.GlobalSecondaryIndexUpdate
6060
}{
61-
{ // No-op
61+
{ // No-op => no changes
6262
Old: []interface{}{
6363
map[string]interface{}{
6464
"name": "att1-index",
@@ -79,6 +79,29 @@ func TestDiffDynamoDbGSI(t *testing.T) {
7979
},
8080
ExpectedUpdates: []*dynamodb.GlobalSecondaryIndexUpdate{},
8181
},
82+
{ // No-op => ignore ordering of non_key_attributes
83+
Old: []interface{}{
84+
map[string]interface{}{
85+
"name": "att1-index",
86+
"hash_key": "att1",
87+
"write_capacity": 10,
88+
"read_capacity": 10,
89+
"projection_type": "INCLUDE",
90+
"non_key_attributes": []interface{}{"attr3", "attr1", "attr2"},
91+
},
92+
},
93+
New: []interface{}{
94+
map[string]interface{}{
95+
"name": "att1-index",
96+
"hash_key": "att1",
97+
"write_capacity": 10,
98+
"read_capacity": 10,
99+
"projection_type": "INCLUDE",
100+
"non_key_attributes": []interface{}{"attr1", "attr2", "attr3"},
101+
},
102+
},
103+
ExpectedUpdates: []*dynamodb.GlobalSecondaryIndexUpdate{},
104+
},
82105

83106
{ // Creation
84107
Old: []interface{}{

aws/structure.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4165,17 +4165,31 @@ func diffDynamoDbGSI(oldGsi, newGsi []interface{}, billingMode string) (ops []*d
41654165
newWriteCapacity, newReadCapacity := newMap["write_capacity"].(int), newMap["read_capacity"].(int)
41664166
capacityChanged := (oldWriteCapacity != newWriteCapacity || oldReadCapacity != newReadCapacity)
41674167

4168+
// pluck non_key_attributes from oldAttributes and newAttributes as reflect.DeepEquals will compare
4169+
// ordinal of elements in its equality (which we actually don't care about)
4170+
nonKeyAttributesChanged := checkIfNonKeyAttributesChanged(oldMap, newMap)
4171+
41684172
oldAttributes, err := stripCapacityAttributes(oldMap)
41694173
if err != nil {
41704174
e = err
41714175
return
41724176
}
4177+
oldAttributes, err = stripNonKeyAttributes(oldAttributes)
4178+
if err != nil {
4179+
e = err
4180+
return
4181+
}
41734182
newAttributes, err := stripCapacityAttributes(newMap)
41744183
if err != nil {
41754184
e = err
41764185
return
41774186
}
4178-
otherAttributesChanged := !reflect.DeepEqual(oldAttributes, newAttributes)
4187+
newAttributes, err = stripNonKeyAttributes(newAttributes)
4188+
if err != nil {
4189+
e = err
4190+
return
4191+
}
4192+
otherAttributesChanged := nonKeyAttributesChanged || !reflect.DeepEqual(oldAttributes, newAttributes)
41794193

41804194
if capacityChanged && !otherAttributesChanged {
41814195
update := &dynamodb.GlobalSecondaryIndexUpdate{
@@ -4214,6 +4228,49 @@ func diffDynamoDbGSI(oldGsi, newGsi []interface{}, billingMode string) (ops []*d
42144228
return
42154229
}
42164230

4231+
func stripNonKeyAttributes(in map[string]interface{}) (map[string]interface{}, error) {
4232+
mapCopy, err := copystructure.Copy(in)
4233+
if err != nil {
4234+
return nil, err
4235+
}
4236+
4237+
m := mapCopy.(map[string]interface{})
4238+
4239+
delete(m, "non_key_attributes")
4240+
4241+
return m, nil
4242+
}
4243+
4244+
// checkIfNonKeyAttributesChanged returns true if non_key_attributes between old map and new map are different
4245+
func checkIfNonKeyAttributesChanged(oldMap, newMap map[string]interface{}) bool {
4246+
oldNonKeyAttributes, oldNkaExists := oldMap["non_key_attributes"].([]string)
4247+
newNonKeyAttributes, newNkaExists := newMap["non_key_attributes"].([]string)
4248+
if oldNkaExists != newNkaExists {
4249+
return true
4250+
}
4251+
4252+
o := map[string]bool{}
4253+
for _, oldNonKeyAttribute := range oldNonKeyAttributes {
4254+
o[oldNonKeyAttribute] = true
4255+
}
4256+
n := map[string]bool{}
4257+
for _, newNonKeyAttribute := range newNonKeyAttributes {
4258+
n[newNonKeyAttribute] = true
4259+
}
4260+
4261+
// perform this check after populating map so we ignore duplicated fields in non_key_attributes
4262+
if len(o) != len(n) {
4263+
return true
4264+
}
4265+
4266+
for k, v := range n {
4267+
if oVal, oExists := o[k]; !oExists || v != oVal {
4268+
return true
4269+
}
4270+
}
4271+
return false
4272+
}
4273+
42174274
func stripCapacityAttributes(in map[string]interface{}) (map[string]interface{}, error) {
42184275
mapCopy, err := copystructure.Copy(in)
42194276
if err != nil {
@@ -4474,6 +4531,10 @@ func expandDynamoDbProjection(data map[string]interface{}) *dynamodb.Projection
44744531
projection.NonKeyAttributes = expandStringList(v)
44754532
}
44764533

4534+
if v, ok := data["non_key_attributes"].(*schema.Set); ok && v.Len() > 0 {
4535+
projection.NonKeyAttributes = expandStringList(v.List())
4536+
}
4537+
44774538
return projection
44784539
}
44794540

0 commit comments

Comments
 (0)