@@ -208,10 +208,26 @@ func (rm *resourceManager) customUpdateTable(
208
208
}
209
209
}
210
210
211
+ addedGSIs , updatedGSIs , removedGSIs := computeGlobalSecondaryIndexDelta (
212
+ latest .ko .Spec .GlobalSecondaryIndexes ,
213
+ desired .ko .Spec .GlobalSecondaryIndexes ,
214
+ )
215
+
216
+ // Delete GSIs that have been removed first to avoid errors when updating table properties
217
+ // where required values have not been set for removed GSIs.
218
+ if delta .DifferentAt ("Spec.GlobalSecondaryIndexes" ) && len (removedGSIs ) > 0 {
219
+ err = rm .deleteGSIs (ctx , desired , latest , removedGSIs )
220
+ if err != nil {
221
+ return nil , err
222
+ }
223
+ }
224
+
225
+ // If billing mode changing from PAY_PER_REQUEST to PROVISIONED, need to include all GSI updates. Otherwise,
226
+ // need to perform GSI updates one at a time afterwards.
211
227
if delta .DifferentAt ("Spec.BillingMode" ) ||
212
228
delta .DifferentAt ("Spec.TableClass" ) || delta .DifferentAt ("Spec.DeletionProtectionEnabled" ) {
213
- if err := rm .syncTable (ctx , desired , delta ); err != nil {
214
- return nil , fmt . Errorf ( "cannot update table %v" , err )
229
+ if err := rm .syncTable (ctx , desired , latest , delta ); err != nil {
230
+ return nil , err
215
231
}
216
232
}
217
233
@@ -229,25 +245,29 @@ func (rm *resourceManager) customUpdateTable(
229
245
}
230
246
}
231
247
248
+ // Update any GSIs that have been modified.
249
+ if delta .DifferentAt ("Spec.GlobalSecondaryIndexes" ) && len (updatedGSIs ) > 0 {
250
+ if err := rm .updateGSIs (ctx , desired , latest , updatedGSIs ); err != nil {
251
+ return nil , err
252
+ }
253
+ }
254
+
232
255
// We want to update fast fields first
233
256
// Then attributes
234
257
// then GSI
235
258
if delta .DifferentExcept ("Spec.Tags" , "Spec.TimeToLive" ) {
236
259
switch {
237
260
case delta .DifferentAt ("Spec.StreamSpecification" ):
238
- if err := rm .syncTable (ctx , desired , delta ); err != nil {
261
+ if err := rm .syncTable (ctx , desired , latest , delta ); err != nil {
239
262
return nil , err
240
263
}
241
264
case delta .DifferentAt ("Spec.ProvisionedThroughput" ):
242
265
if err := rm .syncTableProvisionedThroughput (ctx , desired ); err != nil {
243
266
return nil , err
244
267
}
245
- case delta .DifferentAt ("Spec.GlobalSecondaryIndexes" ):
246
- if err := rm .syncTableGlobalSecondaryIndexes (ctx , latest , desired ); err != nil {
247
- if awsErr , ok := ackerr .AWSError (err ); ok &&
248
- awsErr .ErrorCode () == "LimitExceededException" {
249
- return nil , requeueWaitGSIReady
250
- }
268
+ // Create any new GSIs once all existing GSI have been updated.
269
+ case delta .DifferentAt ("Spec.GlobalSecondaryIndexes" ) && len (addedGSIs ) > 0 :
270
+ if err := rm .addGSIs (ctx , desired , latest , addedGSIs ); err != nil {
251
271
return nil , err
252
272
}
253
273
case delta .DifferentAt ("Spec.TableReplicas" ):
@@ -271,74 +291,105 @@ func (rm *resourceManager) customUpdateTable(
271
291
// or SSE specification.
272
292
func (rm * resourceManager ) syncTable (
273
293
ctx context.Context ,
274
- r * resource ,
294
+ desired * resource ,
295
+ latest * resource ,
275
296
delta * ackcompare.Delta ,
276
297
) (err error ) {
277
298
rlog := ackrtlog .FromContext (ctx )
278
299
exit := rlog .Trace ("rm.syncTable" )
279
300
defer exit (err )
280
301
281
- input , err := rm .newUpdateTablePayload (ctx , r , delta )
302
+ input , err := rm .newUpdateTablePayload (ctx , desired , latest , delta )
282
303
if err != nil {
283
304
return err
284
305
}
285
306
_ , err = rm .sdkapi .UpdateTable (ctx , input )
286
307
rm .metrics .RecordAPICall ("UPDATE" , "UpdateTable" , err )
287
308
if err != nil {
288
- return err
309
+ return fmt . Errorf ( "cannot update table %v" , err )
289
310
}
311
+ // If GSI update were included in the table update we need to requeue.
312
+ if len (input .GlobalSecondaryIndexUpdates ) > 0 {
313
+ return requeueWaitGSIReady
314
+ }
315
+
290
316
return nil
291
317
}
292
318
293
319
// newUpdateTablePayload constructs the updateTableInput object.
294
320
func (rm * resourceManager ) newUpdateTablePayload (
295
321
ctx context.Context ,
296
- r * resource ,
322
+ desired * resource ,
323
+ latest * resource ,
297
324
delta * ackcompare.Delta ,
298
325
) (* svcsdk.UpdateTableInput , error ) {
299
326
input := & svcsdk.UpdateTableInput {
300
- TableName : aws .String (* r .ko .Spec .TableName ),
327
+ TableName : aws .String (* desired .ko .Spec .TableName ),
301
328
}
302
-
329
+ latestBillingMode := svcsdktypes . BillingMode ( * latest . ko . Spec . BillingMode )
303
330
if delta .DifferentAt ("Spec.BillingMode" ) {
304
- if r .ko .Spec .BillingMode != nil {
305
- input .BillingMode = svcsdktypes .BillingMode (* r .ko .Spec .BillingMode )
331
+ if desired .ko .Spec .BillingMode != nil {
332
+ input .BillingMode = svcsdktypes .BillingMode (* desired .ko .Spec .BillingMode )
306
333
} else {
307
334
// set biling mode to the default value `PROVISIONED`
308
335
input .BillingMode = svcsdktypes .BillingModeProvisioned
309
336
}
337
+
310
338
if input .BillingMode == svcsdktypes .BillingModeProvisioned {
311
339
input .ProvisionedThroughput = & svcsdktypes.ProvisionedThroughput {}
312
- if r .ko .Spec .ProvisionedThroughput != nil {
313
- if r .ko .Spec .ProvisionedThroughput .ReadCapacityUnits != nil {
340
+ if desired .ko .Spec .ProvisionedThroughput != nil {
341
+ if desired .ko .Spec .ProvisionedThroughput .ReadCapacityUnits != nil {
314
342
input .ProvisionedThroughput .ReadCapacityUnits = aws .Int64 (
315
- * r .ko .Spec .ProvisionedThroughput .ReadCapacityUnits ,
343
+ * desired .ko .Spec .ProvisionedThroughput .ReadCapacityUnits ,
316
344
)
317
345
} else {
318
346
input .ProvisionedThroughput .ReadCapacityUnits = aws .Int64 (0 )
319
347
}
320
348
321
- if r .ko .Spec .ProvisionedThroughput .WriteCapacityUnits != nil {
349
+ if desired .ko .Spec .ProvisionedThroughput .WriteCapacityUnits != nil {
322
350
input .ProvisionedThroughput .WriteCapacityUnits = aws .Int64 (
323
- * r .ko .Spec .ProvisionedThroughput .WriteCapacityUnits ,
351
+ * desired .ko .Spec .ProvisionedThroughput .WriteCapacityUnits ,
324
352
)
325
353
} else {
326
354
input .ProvisionedThroughput .WriteCapacityUnits = aws .Int64 (0 )
327
355
}
328
356
}
329
357
}
358
+
359
+ // If billing mode is changing from PAY_PER_REQUEST to PROVISIONED we need to include all GSI updates
360
+ if latestBillingMode == svcsdktypes .BillingModePayPerRequest && input .BillingMode == svcsdktypes .BillingModeProvisioned {
361
+ _ , updatedGSIs , _ := computeGlobalSecondaryIndexDelta (
362
+ latest .ko .Spec .GlobalSecondaryIndexes ,
363
+ desired .ko .Spec .GlobalSecondaryIndexes ,
364
+ )
365
+
366
+ // DynamoDB API fails if GSI updates are empty. Only set GlobalSecondaryIndexUpdates
367
+ // if there are GSIs to update.
368
+ if len (updatedGSIs ) > 0 {
369
+ input .GlobalSecondaryIndexUpdates = []svcsdktypes.GlobalSecondaryIndexUpdate {}
370
+ for _ , updatedGSI := range updatedGSIs {
371
+ update := svcsdktypes.GlobalSecondaryIndexUpdate {
372
+ Update : & svcsdktypes.UpdateGlobalSecondaryIndexAction {
373
+ IndexName : aws .String (* updatedGSI .IndexName ),
374
+ ProvisionedThroughput : newSDKProvisionedThroughput (updatedGSI .ProvisionedThroughput ),
375
+ },
376
+ }
377
+ input .GlobalSecondaryIndexUpdates = append (input .GlobalSecondaryIndexUpdates , update )
378
+ }
379
+ }
380
+ }
330
381
}
331
382
if delta .DifferentAt ("Spec.StreamSpecification" ) {
332
- if r .ko .Spec .StreamSpecification != nil {
333
- if r .ko .Spec .StreamSpecification .StreamEnabled != nil {
383
+ if desired .ko .Spec .StreamSpecification != nil {
384
+ if desired .ko .Spec .StreamSpecification .StreamEnabled != nil {
334
385
input .StreamSpecification = & svcsdktypes.StreamSpecification {
335
- StreamEnabled : aws .Bool (* r .ko .Spec .StreamSpecification .StreamEnabled ),
386
+ StreamEnabled : aws .Bool (* desired .ko .Spec .StreamSpecification .StreamEnabled ),
336
387
}
337
388
// Only set streamViewType when streamSpefication is enabled and streamViewType is non-nil.
338
- if * r .ko .Spec .StreamSpecification .StreamEnabled &&
339
- r .ko .Spec .StreamSpecification .StreamViewType != nil {
389
+ if * desired .ko .Spec .StreamSpecification .StreamEnabled &&
390
+ desired .ko .Spec .StreamSpecification .StreamViewType != nil {
340
391
input .StreamSpecification .StreamViewType = svcsdktypes .StreamViewType (
341
- * r .ko .Spec .StreamSpecification .StreamViewType ,
392
+ * desired .ko .Spec .StreamSpecification .StreamViewType ,
342
393
)
343
394
}
344
395
} else {
@@ -349,13 +400,13 @@ func (rm *resourceManager) newUpdateTablePayload(
349
400
}
350
401
}
351
402
if delta .DifferentAt ("Spec.TableClass" ) {
352
- if r .ko .Spec .TableClass != nil {
353
- input .TableClass = svcsdktypes .TableClass (* r .ko .Spec .TableClass )
403
+ if desired .ko .Spec .TableClass != nil {
404
+ input .TableClass = svcsdktypes .TableClass (* desired .ko .Spec .TableClass )
354
405
}
355
406
}
356
407
357
408
if delta .DifferentAt ("Spec.DeletionProtectionEnabled" ) {
358
- input .DeletionProtectionEnabled = aws .Bool (* r .ko .Spec .DeletionProtectionEnabled )
409
+ input .DeletionProtectionEnabled = aws .Bool (* desired .ko .Spec .DeletionProtectionEnabled )
359
410
}
360
411
361
412
return input , nil
0 commit comments