Skip to content

Commit

Permalink
feat: add flag to disable gsi limit check in iterative deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
alharris-at committed Aug 30, 2023
1 parent 066d4ed commit f3b3758
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe('DynamoDB GSI Utils', () => {
const tableWithNoGSI = makeTableWithGSI({
gsis: [],
});
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithNoGSI);
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithNoGSI, false);
expect(updatedTable).toBeDefined();
expect(updatedTable).not.toEqual(tableWithNoGSI);
expect(updatedTable.Properties.AttributeDefinitions).toEqual([
Expand All @@ -136,7 +136,7 @@ describe('DynamoDB GSI Utils', () => {
},
],
});
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithGSI);
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithGSI, false);
expect(updatedTable).toBeDefined();
expect(updatedTable).not.toEqual(tableWithGSI);
expect(updatedTable.Properties.AttributeDefinitions).toEqual([
Expand Down Expand Up @@ -170,7 +170,9 @@ describe('DynamoDB GSI Utils', () => {
},
],
});
expect(() => gsiUtils.addGSI(gsiItem, tableWithGSI)).toThrowError(`An index with name ${gsiItem.gsi.IndexName} already exists`);
expect(() => gsiUtils.addGSI(gsiItem, tableWithGSI, false)).toThrowError(
`An index with name ${gsiItem.gsi.IndexName} already exists`,
);
});

it(`should throw error when adding new index to a table with ${gsiUtils.MAX_GSI_PER_TABLE} GSIs`, () => {
Expand All @@ -187,11 +189,28 @@ describe('DynamoDB GSI Utils', () => {
];
}, []),
});
expect(() => gsiUtils.addGSI(gsiItem, tableWithMaxGSI)).toThrowError(
expect(() => gsiUtils.addGSI(gsiItem, tableWithMaxGSI, false)).toThrowError(
`DynamoDB ${tableWithMaxGSI.Properties.TableName} can have max of ${gsiUtils.MAX_GSI_PER_TABLE} GSIs`,
);
});

it(`should not throw error when adding new index to a table with ${gsiUtils.MAX_GSI_PER_TABLE} GSIs if disableGsiLimitcheck is configured`, () => {
const tableWithMaxGSI = makeTableWithGSI({
gsis: new Array(gsiUtils.MAX_GSI_PER_TABLE).fill(0).reduce((acc, i, idx) => {
return [
...acc,
{
indexName: `byTitile${idx}AndId`,
attributes: {
hash: { name: `title${idx}` },
},
},
];
}, []),
});
expect(() => gsiUtils.addGSI(gsiItem, tableWithMaxGSI, true)).not.toThrowError();
});

it('should not have duplicate AttributeDefinitions', () => {
const tableWithGSI = makeTableWithGSI({
gsis: [
Expand All @@ -210,7 +229,7 @@ describe('DynamoDB GSI Utils', () => {
},
],
});
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithGSI);
const updatedTable = gsiUtils.addGSI(gsiItem, tableWithGSI, false);
expect(updatedTable).toBeDefined();
expect(updatedTable).not.toEqual(tableWithGSI);
expect(updatedTable.Properties.AttributeDefinitions).toEqual([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { addGSI, getGSIDetails, removeGSI } from './dynamodb-gsi-helpers';

import { loadConfiguration } from '../configuration-manager';

const DISABLE_GSI_LIMIT_CHECK_OPTION = 'disable-gsi-limit-check';
const ROOT_LEVEL = 'root';
const RESERVED_ROOT_STACK_TEMPLATE_STATE_KEY_NAME = '_root';
const CONNECTION_STACK_NAME = 'ConnectionStack';
Expand All @@ -43,6 +44,7 @@ export type GQLResourceManagerProps = {
backendDir: string;
cloudBackendDir: string;
rebuildAllTables?: boolean;
disableGSILimitCheck?: boolean;
};

/**
Expand Down Expand Up @@ -76,6 +78,7 @@ export class GraphQLResourceManager {
private backendApiProjectRoot: string;
private templateState: TemplateState;
private rebuildAllTables = false; // indicates that all underlying model tables should be rebuilt
private readonly disableGSILimitCheck;

public static createInstance = async (
context: $TSContext,
Expand All @@ -89,12 +92,14 @@ export class GraphQLResourceManager {
const apiStack = await cfn
.describeStackResources({ StackName: StackId, LogicalResourceId: gqlResource.providerMetadata.logicalId })
.promise();

return new GraphQLResourceManager({
cfnClient: cfn,
resourceMeta: { ...gqlResource, stackId: apiStack.StackResources[0].PhysicalResourceId },
backendDir: pathManager.getBackendDirPath(),
cloudBackendDir: pathManager.getCurrentCloudBackendDirPath(),
rebuildAllTables,
disableGSILimitCheck: context.input.options[DISABLE_GSI_LIMIT_CHECK_OPTION],
});
};

Expand All @@ -113,6 +118,7 @@ export class GraphQLResourceManager {
this.cloudBackendApiProjectRoot = path.join(props.cloudBackendDir, GraphQLResourceManager.categoryName, this.resourceMeta.resourceName);
this.templateState = new TemplateState();
this.rebuildAllTables = props.rebuildAllTables || false;
this.disableGSILimitCheck = props.disableGSILimitCheck || false;
}

run = async (): Promise<DeploymentStep[]> => {
Expand Down Expand Up @@ -438,7 +444,7 @@ export class GraphQLResourceManager {

private addGSI = (gsiRecord: GSIRecord, tableName: string, template: Template): void => {
const table = template.Resources[tableName] as DynamoDB.Table;
template.Resources[tableName] = addGSI(gsiRecord, table);
template.Resources[tableName] = addGSI(gsiRecord, table, this.disableGSILimitCheck);
};

private deleteGSI = (indexName: string, tableName: string, template: Template): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,21 @@ export const getGSIDetails = (indexName: string, table: DynamoDB.Table): GSIReco
* Helper method to add new GSI and attribute definitions
* @param index GSIRecord with the index and attribute definition
* @param table DynamoDB table to which the new GSI is added
* @param disableGSILimitChecks if enabled, do not check for GSI limits during iteration.
*/
export const addGSI = (index: GSIRecord, table: DynamoDB.Table): DynamoDB.Table => {
export const addGSI = (index: GSIRecord, table: DynamoDB.Table, disableGSILimitCheck: boolean): DynamoDB.Table => {
const updatedTable = _.cloneDeep(table);

const gsis = updatedTable.Properties.GlobalSecondaryIndexes ?? [];
assertNotIntrinsicFunction<GlobalSecondaryIndex>(gsis);

const existingIndices = getExistingIndexNames(table);

if (existingIndices.length + 1 > MAX_GSI_PER_TABLE) {
if (existingIndices.length + 1 > MAX_GSI_PER_TABLE && !disableGSILimitCheck) {
const tableName = table.Properties.TableName;
const tableNameString = tableName ? (typeof tableName === 'string' ? tableName : JSON.stringify(tableName)) : '{UnNamedTable}';
throw new AmplifyError('ConfigurationError', {
message: `DynamoDB ${table.Properties.TableName || '{UnNamedTable}'} can have max of ${MAX_GSI_PER_TABLE} GSIs`,
message: `DynamoDB ${tableNameString} can have max of ${MAX_GSI_PER_TABLE} GSIs`,
});
}

Expand Down

0 comments on commit f3b3758

Please sign in to comment.