Skip to content
This repository was archived by the owner on Dec 1, 2022. It is now read-only.

Commit 050841e

Browse files
committed
Add throttled events into the provisioning calculations (#41)
Fix minimum config contact issue (#40) Added throttled events logging (#32)
1 parent 945af44 commit 050841e

File tree

11 files changed

+189
-115
lines changed

11 files changed

+189
-115
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"check": "flow check",
2222
"build": "gulp dist",
2323
"start": "node ./scripts/start.js",
24-
"debug": "node-debug ./scripts/start.js"
24+
"debug": "node --inspect --debug-brk ./scripts/start.js"
2525
},
2626
"dependencies": {},
2727
"devDependencies": {

src/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default class App {
7171

7272
log('Getting table consumed capacity description', tableName);
7373
let consumedCapacityTableDescription = await this._capacityCalculator
74-
.describeTableConsumedCapacityAsync(tableDescription, 1);
74+
.describeTableConsumedCapacityAsync(tableDescription);
7575

7676
log('Getting table update request', tableName);
7777
let tableUpdateRequest = await this._provisioner.getTableUpdateAsync(tableDescription,

src/CapacityCalculator.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export default class CapacityCalculator extends CapacityCalculatorBase {
2020
};
2121
}
2222

23+
getThrottledEventStatisticSettings(): StatisticSettings {
24+
return {
25+
count: 1,
26+
spanMinutes: 1,
27+
type: 'Sum',
28+
};
29+
}
30+
2331
// Gets the projected capacity value based on the cloudwatch datapoints
2432
getProjectedValue(settings: StatisticSettings, data: GetMetricStatisticsResponse) {
2533
invariant(data != null, 'Parameter \'data\' is not set');

src/Provisioner.js

Lines changed: 82 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ export default class Provisioner extends ProvisionerConfigurableBase {
4747
invariant(data != null, 'Parameter \'data\' is not set');
4848

4949
let config = this.getTableConfig(data);
50-
if (config.ReadCapacity.Increment == null) {
51-
return false;
52-
}
53-
5450
let adjustmentContext = this.getReadCapacityIncrementAdjustmentContext(data, config);
5551
return this.isCapacityAdjustmentRequired(data, adjustmentContext);
5652
}
@@ -67,12 +63,8 @@ export default class Provisioner extends ProvisionerConfigurableBase {
6763
invariant(data != null, 'Parameter \'data\' is not set');
6864

6965
let config = this.getTableConfig(data);
70-
if (config.ReadCapacity.Decrement == null) {
71-
return false;
72-
}
73-
7466
let adjustmentContext = this.getReadCapacityDecrementAdjustmentContext(data, config);
75-
return this.isCapacityAdjustmentRequired(data, adjustmentContext, d => this.calculateDecrementedReadCapacityValue(d));
67+
return this.isCapacityAdjustmentRequired(data, adjustmentContext);
7668
}
7769

7870
calculateDecrementedReadCapacityValue(data: TableProvisionedAndConsumedThroughput): number {
@@ -87,10 +79,6 @@ export default class Provisioner extends ProvisionerConfigurableBase {
8779
invariant(data != null, 'Parameter \'data\' is not set');
8880

8981
let config = this.getTableConfig(data);
90-
if (config.WriteCapacity.Increment == null) {
91-
return false;
92-
}
93-
9482
let adjustmentContext = this.getWriteCapacityIncrementAdjustmentContext(data, config);
9583
return this.isCapacityAdjustmentRequired(data, adjustmentContext);
9684
}
@@ -107,12 +95,8 @@ export default class Provisioner extends ProvisionerConfigurableBase {
10795
invariant(data != null, 'Parameter \'data\' is not set');
10896

10997
let config = this.getTableConfig(data);
110-
if (config.WriteCapacity.Decrement == null) {
111-
return false;
112-
}
113-
11498
let adjustmentContext = this.getWriteCapacityDecrementAdjustmentContext(data, config);
115-
return this.isCapacityAdjustmentRequired(data, adjustmentContext, this.calculateDecrementedWriteCapacityValue);
99+
return this.isCapacityAdjustmentRequired(data, adjustmentContext);
116100
}
117101

118102
calculateDecrementedWriteCapacityValue(data: TableProvisionedAndConsumedThroughput): number {
@@ -124,85 +108,122 @@ export default class Provisioner extends ProvisionerConfigurableBase {
124108
}
125109

126110
getReadCapacityIncrementAdjustmentContext(data: TableProvisionedAndConsumedThroughput, config: ProvisionerConfig): AdjustmentContext {
127-
invariant(config.ReadCapacity.Increment != null, 'Increment cannot be null');
111+
invariant(data != null, 'Argument \'data\' cannot be null');
112+
invariant(config != null, 'Argument \'config\' cannot be null');
128113

129-
return {
114+
let context = {
130115
TableName: data.TableName,
131116
IndexName: data.IndexName,
132117
CapacityType: 'read',
133118
AdjustmentType: 'increment',
134119
ProvisionedValue: data.ProvisionedThroughput.ReadCapacityUnits,
135120
ConsumedValue: data.ConsumedThroughput.ReadCapacityUnits,
121+
ThrottledEvents: data.ThrottledEvents.ThrottledReadEvents,
136122
UtilisationPercent: (data.ConsumedThroughput.ReadCapacityUnits / data.ProvisionedThroughput.ReadCapacityUnits) * 100,
137123
CapacityConfig: config.ReadCapacity,
138-
CapacityAdjustmentConfig: config.ReadCapacity.Increment,
139124
};
125+
126+
if (config.ReadCapacity.Increment != null) {
127+
// $FlowIgnore
128+
context.CapacityAdjustmentConfig = config.ReadCapacity.Increment;
129+
}
130+
131+
return context;
140132
}
141133

142134
getReadCapacityDecrementAdjustmentContext(data: TableProvisionedAndConsumedThroughput, config: ProvisionerConfig): AdjustmentContext {
143-
invariant(config.ReadCapacity.Decrement != null, 'Decrement cannot be null');
135+
invariant(data != null, 'Argument \'data\' cannot be null');
136+
invariant(config != null, 'Argument \'config\' cannot be null');
144137

145-
return {
138+
let context = {
146139
TableName: data.TableName,
147140
IndexName: data.IndexName,
148141
CapacityType: 'read',
149142
AdjustmentType: 'decrement',
150143
ProvisionedValue: data.ProvisionedThroughput.ReadCapacityUnits,
151144
ConsumedValue: data.ConsumedThroughput.ReadCapacityUnits,
145+
ThrottledEvents: data.ThrottledEvents.ThrottledReadEvents,
152146
UtilisationPercent: (data.ConsumedThroughput.ReadCapacityUnits / data.ProvisionedThroughput.ReadCapacityUnits) * 100,
153147
CapacityConfig: config.ReadCapacity,
154-
CapacityAdjustmentConfig: config.ReadCapacity.Decrement,
155148
};
149+
150+
if (config.ReadCapacity.Decrement != null) {
151+
// $FlowIgnore
152+
context.CapacityAdjustmentConfig = config.ReadCapacity.Decrement;
153+
}
154+
155+
return context;
156156
}
157157

158158
getWriteCapacityIncrementAdjustmentContext(data: TableProvisionedAndConsumedThroughput, config: ProvisionerConfig): AdjustmentContext {
159-
invariant(config.WriteCapacity.Increment != null, 'Increment cannot be null');
159+
invariant(data != null, 'Argument \'data\' cannot be null');
160+
invariant(config != null, 'Argument \'config\' cannot be null');
160161

161-
return {
162+
let context = {
162163
TableName: data.TableName,
163164
IndexName: data.IndexName,
164165
CapacityType: 'write',
165166
AdjustmentType: 'increment',
166167
ProvisionedValue: data.ProvisionedThroughput.WriteCapacityUnits,
167168
ConsumedValue: data.ConsumedThroughput.WriteCapacityUnits,
169+
ThrottledEvents: data.ThrottledEvents.ThrottledWriteEvents,
168170
UtilisationPercent: (data.ConsumedThroughput.WriteCapacityUnits / data.ProvisionedThroughput.WriteCapacityUnits) * 100,
169171
CapacityConfig: config.WriteCapacity,
170-
CapacityAdjustmentConfig: config.WriteCapacity.Increment,
171172
};
173+
174+
if (config.WriteCapacity.Increment != null) {
175+
// $FlowIgnore
176+
context.CapacityAdjustmentConfig = config.WriteCapacity.Increment;
177+
}
178+
179+
return context;
172180
}
173181

174182
getWriteCapacityDecrementAdjustmentContext(data: TableProvisionedAndConsumedThroughput, config: ProvisionerConfig): AdjustmentContext {
175-
invariant(config.WriteCapacity.Decrement != null, 'Decrement cannot be null');
183+
invariant(data != null, 'Argument \'data\' cannot be null');
184+
invariant(config != null, 'Argument \'config\' cannot be null');
176185

177-
return {
186+
let context = {
178187
TableName: data.TableName,
179188
IndexName: data.IndexName,
180189
CapacityType: 'write',
181190
AdjustmentType: 'decrement',
182191
ProvisionedValue: data.ProvisionedThroughput.WriteCapacityUnits,
183192
ConsumedValue: data.ConsumedThroughput.WriteCapacityUnits,
193+
ThrottledEvents: data.ThrottledEvents.ThrottledWriteEvents,
184194
UtilisationPercent: (data.ConsumedThroughput.WriteCapacityUnits / data.ProvisionedThroughput.WriteCapacityUnits) * 100,
185195
CapacityConfig: config.WriteCapacity,
186-
CapacityAdjustmentConfig: config.WriteCapacity.Decrement,
187196
};
197+
198+
if (config.WriteCapacity.Decrement != null) {
199+
// $FlowIgnore
200+
context.CapacityAdjustmentConfig = config.WriteCapacity.Decrement;
201+
}
202+
203+
return context;
188204
}
189205

190206
isCapacityAdjustmentRequired(data: TableProvisionedAndConsumedThroughput, adjustmentContext: AdjustmentContext): boolean {
191207

192208
// Determine if an adjustment is wanted
193-
let isAboveMax = this.isAboveMax(adjustmentContext);
194-
let isBelowMin = this.isBelowMin(adjustmentContext);
195-
let isAboveThreshold = this.isAboveThreshold(adjustmentContext);
196-
let isBelowThreshold = this.isBelowThreshold(adjustmentContext);
197-
let isAdjustmentWanted = (isAboveMax || isBelowMin || isAboveThreshold || isBelowThreshold);
209+
let isProvAboveMax = adjustmentContext.CapacityConfig.Max == null ? false : adjustmentContext.ProvisionedValue > adjustmentContext.CapacityConfig.Max;
210+
let isProvBelowMax = adjustmentContext.CapacityConfig.Max == null ? true : adjustmentContext.ProvisionedValue < adjustmentContext.CapacityConfig.Max;
211+
let isProvBelowMin = adjustmentContext.CapacityConfig.Min == null ? adjustmentContext.ProvisionedValue < 1 : adjustmentContext.ProvisionedValue < adjustmentContext.CapacityConfig.Min;
212+
let isProvAboveMin = adjustmentContext.CapacityConfig.Min == null ? adjustmentContext.ProvisionedValue > 1 : adjustmentContext.ProvisionedValue > adjustmentContext.CapacityConfig.Min;
213+
let isUtilAboveThreshold = this.isAboveThreshold(adjustmentContext);
214+
let isUtilBelowThreshold = this.isBelowThreshold(adjustmentContext);
215+
let isThrottledEventsAboveThreshold = this.isThrottledEventsAboveThreshold(adjustmentContext);
216+
let isAdjustmentWanted = adjustmentContext.AdjustmentType === 'increment' ?
217+
(isProvBelowMin || isUtilAboveThreshold || isUtilBelowThreshold || isThrottledEventsAboveThreshold) && isProvBelowMax :
218+
(isProvAboveMax || isUtilAboveThreshold || isUtilBelowThreshold) && isProvAboveMin;
198219

199220
// Determine if an adjustment is allowed under the rate limiting rules
200-
let isAfterLastDecreaseGracePeriod = this.isAfterLastAdjustmentGracePeriod(
201-
data.ProvisionedThroughput.LastDecreaseDateTime,
202-
adjustmentContext.CapacityAdjustmentConfig.When.AfterLastDecrementMinutes);
203-
let isAfterLastIncreaseGracePeriod = this.isAfterLastAdjustmentGracePeriod(
204-
data.ProvisionedThroughput.LastIncreaseDateTime,
205-
adjustmentContext.CapacityAdjustmentConfig.When.AfterLastIncrementMinutes);
221+
let isAfterLastDecreaseGracePeriod = adjustmentContext.CapacityAdjustmentConfig == null ||
222+
this.isAfterLastAdjustmentGracePeriod(data.ProvisionedThroughput.LastDecreaseDateTime,
223+
adjustmentContext.CapacityAdjustmentConfig.When.AfterLastDecrementMinutes);
224+
let isAfterLastIncreaseGracePeriod = adjustmentContext.CapacityAdjustmentConfig == null ||
225+
this.isAfterLastAdjustmentGracePeriod(data.ProvisionedThroughput.LastIncreaseDateTime,
226+
adjustmentContext.CapacityAdjustmentConfig.When.AfterLastIncrementMinutes);
206227

207228
let isReadDecrementAllowed = adjustmentContext.AdjustmentType === 'decrement' ?
208229
RateLimitedDecrement.isDecrementAllowed(data, adjustmentContext, d => this.calculateDecrementedReadCapacityValue(d)) :
@@ -213,10 +234,11 @@ export default class Provisioner extends ProvisionerConfigurableBase {
213234
// Package up the configuration and the results so that we can produce
214235
// some effective logs
215236
let adjustmentData = {
216-
isAboveMax,
217-
isBelowMin,
218-
isAboveThreshold,
219-
isBelowThreshold,
237+
isAboveMax: isProvAboveMax,
238+
isBelowMin: isProvBelowMin,
239+
isAboveThreshold: isUtilAboveThreshold,
240+
isBelowThreshold: isUtilBelowThreshold,
241+
isAboveThrottledEventThreshold: isThrottledEventsAboveThreshold,
220242
isAfterLastDecreaseGracePeriod,
221243
isAfterLastIncreaseGracePeriod,
222244
isAdjustmentWanted,
@@ -228,58 +250,41 @@ export default class Provisioner extends ProvisionerConfigurableBase {
228250
return isAdjustmentWanted && isAdjustmentAllowed;
229251
}
230252

231-
isAboveThreshold(context: AdjustmentContext): boolean {
253+
isThrottledEventsAboveThreshold(context: AdjustmentContext): boolean {
232254
invariant(context != null, 'Parameter \'context\' is not set');
233255

234-
if (context.CapacityAdjustmentConfig.When.UtilisationIsAbovePercent == null) {
235-
return false;
236-
}
237-
238-
if (context.CapacityConfig.Max != null &&
239-
context.ProvisionedValue >= context.CapacityConfig.Max) {
240-
// Already at maximum allowed ProvisionedValue
256+
if (context.CapacityAdjustmentConfig == null ||
257+
context.CapacityAdjustmentConfig.When.ThrottledEventsPerMinuteIsAbove == null ||
258+
context.AdjustmentType === 'decrement') {
241259
return false;
242260
}
243261

244-
let utilisationPercent = (context.ConsumedValue / context.ProvisionedValue) * 100;
245-
return utilisationPercent > context.CapacityAdjustmentConfig.When.UtilisationIsAbovePercent;
262+
return context.ThrottledEvents >
263+
context.CapacityAdjustmentConfig.When.ThrottledEventsPerMinuteIsAbove;
246264
}
247265

248-
isBelowThreshold(context: AdjustmentContext): boolean {
266+
isAboveThreshold(context: AdjustmentContext): boolean {
249267
invariant(context != null, 'Parameter \'context\' is not set');
250268

251-
if (context.CapacityAdjustmentConfig.When.UtilisationIsBelowPercent == null) {
252-
return false;
253-
}
254-
255-
let min = context.CapacityConfig.Min != null ? context.CapacityConfig.Min : 1;
256-
if (context.ProvisionedValue <= min) {
257-
// Already at minimum allowed ProvisionedValue
269+
if (context.CapacityAdjustmentConfig == null ||
270+
context.CapacityAdjustmentConfig.When.UtilisationIsAbovePercent == null) {
258271
return false;
259272
}
260273

261274
let utilisationPercent = (context.ConsumedValue / context.ProvisionedValue) * 100;
262-
return utilisationPercent < context.CapacityAdjustmentConfig.When.UtilisationIsBelowPercent;
263-
}
264-
265-
isAboveMax(context: AdjustmentContext): boolean {
266-
invariant(context != null, 'Parameter \'context\' is not set');
267-
268-
if (context.CapacityConfig.Max == null) {
269-
return false;
270-
}
271-
272-
return context.ProvisionedValue > context.CapacityConfig.Max;
275+
return utilisationPercent > context.CapacityAdjustmentConfig.When.UtilisationIsAbovePercent;
273276
}
274277

275-
isBelowMin(context: AdjustmentContext): boolean {
278+
isBelowThreshold(context: AdjustmentContext): boolean {
276279
invariant(context != null, 'Parameter \'context\' is not set');
277280

278-
if (context.CapacityConfig.Min == null) {
281+
if (context.CapacityAdjustmentConfig == null ||
282+
context.CapacityAdjustmentConfig.When.UtilisationIsBelowPercent == null) {
279283
return false;
280284
}
281285

282-
return context.ProvisionedValue < context.CapacityConfig.Min;
286+
let utilisationPercent = (context.ConsumedValue / context.ProvisionedValue) * 100;
287+
return utilisationPercent < context.CapacityAdjustmentConfig.When.UtilisationIsBelowPercent;
283288
}
284289

285290
isAfterLastAdjustmentGracePeriod(lastAdjustmentDateTime: string, afterLastAdjustmentMinutes?: number): boolean {

src/capacity/CapacityCalculatorBase.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export default class CapacityCalculatorBase {
2929
invariant(false, 'The method \'getStatisticSettings\' was not implemented');
3030
}
3131

32+
// Gets the settings used to fetch the throttled events statistics
33+
getThrottledEventStatisticSettings(): StatisticSettings {
34+
invariant(false, 'The method \'getThrottledEventStatisticSettings\' was not implemented');
35+
}
36+
3237
// Gets the projected capacity value based on the cloudwatch datapoints
3338
// eslint-disable-next-line no-unused-vars
3439
getProjectedValue(settings: StatisticSettings, data: GetMetricStatisticsResponse): number {
@@ -153,6 +158,13 @@ export default class CapacityCalculatorBase {
153158
value
154159
};
155160

161+
/*
162+
log(JSON.stringify({
163+
...result,
164+
statistics: statistics.Datapoints.map(dp => dp.Sum / (settings.spanMinutes * 60)),
165+
}));
166+
*/
167+
156168
return result;
157169
} catch (ex) {
158170
warning(JSON.stringify({
@@ -171,7 +183,7 @@ export default class CapacityCalculatorBase {
171183
invariant(isRead != null, 'Parameter \'isRead\' is not set');
172184
invariant(tableName != null, 'Parameter \'tableName\' is not set');
173185

174-
let settings = this.getStatisticSettings();
186+
let settings = this.getThrottledEventStatisticSettings();
175187

176188
let EndTime = new Date();
177189
let StartTime = new Date();

0 commit comments

Comments
 (0)