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

Commit 7a107b9

Browse files
committed
Changed the way configuration works to make it more flexible
1 parent 90d5c70 commit 7a107b9

File tree

8 files changed

+163
-185
lines changed

8 files changed

+163
-185
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "index.js",
66
"scripts": {
77
"build": "babel src -d lib",
8-
"start": "node ./scripts/start.js"
8+
"start": "node ./scripts/start.js",
9+
"debug": "node-debug ./scripts/start.js"
910
},
1011
"author": "tmitchel2 <[email protected]>",
1112
"license": "MIT",

src/CapacityCalculator.js

Lines changed: 0 additions & 87 deletions
This file was deleted.

src/ConfigurableProvisioner.js

Lines changed: 43 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import CapacityCalculator from './CapacityCalculator';
21
import Global from './global';
32
const {
43
stats,
@@ -9,34 +8,22 @@ class ConfigurableProvisioner {
98

109
constructor(config) {
1110
this.config = config;
12-
this.capacityCalculator = new CapacityCalculator();
1311
}
1412

1513
getTableUpdate(tableDescription, tableConsumedCapacityDescription) {
1614
try {
1715
logger.debug('ConfigurableProvisioner.getTableUpdate (start)');
1816

19-
let provisionedThroughput = this.getProvisionedThroughput(
20-
tableDescription.Table.ProvisionedThroughput,
21-
tableConsumedCapacityDescription.Table.ConsumedThroughput,
22-
tableDescription.Table.TableName);
17+
let tableData = {
18+
TableName: tableDescription.Table.TableName,
19+
ProvisionedThroughput: tableDescription.Table.ProvisionedThroughput,
20+
ConsumedThroughput: tableConsumedCapacityDescription.Table.ConsumedThroughput
21+
};
2322

23+
let provisionedThroughput = this.getUpdatedProvisionedThroughput(tableData);
2424
let gsis = tableDescription.Table.GlobalSecondaryIndexes || [];
2525
let globalSecondaryIndexUpdates = gsis
26-
.map(gsi => {
27-
let gsicc = tableConsumedCapacityDescription.Table.GlobalSecondaryIndexes.find(i => i.IndexName === gsi.IndexName);
28-
let provisionedThroughput = this.getProvisionedThroughput(gsi.ProvisionedThroughput, gsicc.ConsumedThroughput, tableDescription.Table.TableName, gsicc.IndexName);
29-
if (provisionedThroughput == null) {
30-
return null;
31-
}
32-
33-
return {
34-
Update: {
35-
IndexName: gsi.IndexName,
36-
ProvisionedThroughput: provisionedThroughput
37-
}
38-
};
39-
})
26+
.map(gsi => this.getGlobalSecondaryIndexUpdate(tableDescription, tableConsumedCapacityDescription, gsi))
4027
.filter(i => i != null);
4128

4229
if (!provisionedThroughput && (globalSecondaryIndexUpdates ==null || globalSecondaryIndexUpdates.length === 0)) {
@@ -64,71 +51,53 @@ class ConfigurableProvisioner {
6451
}
6552
}
6653

67-
parseDate(value) {
68-
if (typeof value === 'undefined' || value == null) {
69-
return new Date(-8640000000000000);
70-
}
71-
72-
return Date.parse(value);
73-
}
54+
getGlobalSecondaryIndexUpdate(tableDescription, tableConsumedCapacityDescription, gsi){
55+
let gsicc = tableConsumedCapacityDescription.Table.GlobalSecondaryIndexes.find(i => i.IndexName === gsi.IndexName);
56+
let provisionedThroughput = this.getUpdatedProvisionedThroughput({
57+
TableName: tableDescription.Table.TableName,
58+
IndexName: gsicc.IndexName,
59+
ProvisionedThroughput: gsi.ProvisionedThroughput,
60+
ConsumedThroughput: gsicc.ConsumedThroughput
61+
});
7462

75-
getMax(value) {
76-
if (typeof value === 'undefined') {
77-
return 40000;
63+
if (provisionedThroughput == null) {
64+
return null;
7865
}
7966

80-
return value;
67+
return {
68+
Update: {
69+
IndexName: gsi.IndexName,
70+
ProvisionedThroughput: provisionedThroughput
71+
}
72+
};
8173
}
8274

83-
getMin(value) {
84-
if (typeof value === 'undefined') {
85-
return 1;
75+
getUpdatedProvisionedThroughput(params) {
76+
debugger;
77+
let newProvisionedThroughput = {
78+
ReadCapacityUnits: params.ProvisionedThroughput.ReadCapacityUnits,
79+
WriteCapacityUnits: params.ProvisionedThroughput.WriteCapacityUnits
80+
};
81+
82+
// Adjust read capacity
83+
if (this.config.readCapacity.increment.isAdjustmentRequired(params)){
84+
newProvisionedThroughput.ReadCapacityUnits = this.config.readCapacity.increment.calculateValue(params);
85+
} else if (this.config.readCapacity.decrement.isAdjustmentRequired(params)) {
86+
newProvisionedThroughput.ReadCapacityUnits = this.config.readCapacity.decrement.calculateValue(params);
8687
}
8788

88-
return value;
89-
}
89+
// Adjust write capacity
90+
if (this.config.writeCapacity.increment.isAdjustmentRequired(params)){
91+
newProvisionedThroughput.WriteCapacityUnits = this.config.writeCapacity.increment.calculateValue(params);
92+
} else if (this.config.writeCapacity.decrement.isAdjustmentRequired(params)) {
93+
newProvisionedThroughput.WriteCapacityUnits = this.config.writeCapacity.decrement.calculateValue(params);
94+
}
9095

91-
getProvisionedThroughput(provisionedThroughput, consumedThroughput, tableName, indexName) {
92-
// logger.debug(JSON.stringify({tableName, indexName, provisionedThroughput, consumedThroughput}, null, 2));
93-
94-
let ReadCapacityUnits = this.capacityCalculator.getNewCapacity(
95-
provisionedThroughput.ReadCapacityUnits,
96-
consumedThroughput.ReadCapacityUnits,
97-
this.config.readCapacity.increment.threshold.percent,
98-
this.config.readCapacity.decrement.threshold.percent,
99-
this.config.readCapacity.increment.adjustment.percent,
100-
this.config.readCapacity.decrement.adjustment.percent,
101-
this.getMin(this.config.readCapacity.min),
102-
this.getMax(this.config.readCapacity.max),
103-
provisionedThroughput.NumberOfDecreasesToday,
104-
this.parseDate(provisionedThroughput.LastIncreaseDateTime),
105-
this.parseDate(provisionedThroughput.LastDecreaseDateTime)
106-
);
107-
108-
let WriteCapacityUnits = this.capacityCalculator.getNewCapacity(
109-
provisionedThroughput.WriteCapacityUnits,
110-
consumedThroughput.WriteCapacityUnits,
111-
this.config.writeCapacity.increment.threshold.percent,
112-
this.config.writeCapacity.decrement.threshold.percent,
113-
this.config.writeCapacity.increment.adjustment.percent,
114-
this.config.writeCapacity.decrement.adjustment.percent,
115-
this.getMin(this.config.writeCapacity.min),
116-
this.getMax(this.config.writeCapacity.max),
117-
provisionedThroughput.NumberOfDecreasesToday,
118-
this.parseDate(provisionedThroughput.LastIncreaseDateTime),
119-
this.parseDate(provisionedThroughput.LastDecreaseDateTime)
120-
);
121-
122-
if (ReadCapacityUnits === provisionedThroughput.ReadCapacityUnits
123-
&& WriteCapacityUnits === provisionedThroughput.WriteCapacityUnits) {
96+
if (newProvisionedThroughput.ReadCapacityUnits === params.ProvisionedThroughput.ReadCapacityUnits
97+
&& newProvisionedThroughput.WriteCapacityUnits === params.ProvisionedThroughput.WriteCapacityUnits) {
12498
return null;
12599
}
126100

127-
let newProvisionedThroughput = {
128-
ReadCapacityUnits,
129-
WriteCapacityUnits
130-
};
131-
132101
return newProvisionedThroughput;
133102
}
134103
}

src/DynamoDB.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export default class DynamoDB {
1010
constructor(dynamoOptions, cloudWatchOptions) {
1111
this._db = new AWS.DynamoDB(dynamoOptions);
1212
this._cw = new CloudWatch(cloudWatchOptions);
13-
this._timeframeMinutes = 5;
1413
}
1514

1615
async listTablesAsync() {
@@ -43,14 +42,14 @@ export default class DynamoDB {
4342
}
4443
}
4544

46-
async describeTableConsumedCapacityAsync(params) {
45+
async describeTableConsumedCapacityAsync(params, periodMinutes) {
4746
let sw = stats.timer('DynamoDB.describeTableConsumedCapacityAsync').start();
4847
try {
4948
// Make all the requests concurrently
50-
let tableRead = this.getConsumedCapacityAsync(true, params.TableName, null);
51-
let tableWrite = this.getConsumedCapacityAsync(false, params.TableName, null);
52-
let gsiReads = this.getArrayOrDefault(params.GlobalSecondaryIndexes).map(gsi => this.getConsumedCapacityAsync(true, params.TableName, gsi.IndexName));
53-
let gsiWrites = this.getArrayOrDefault(params.GlobalSecondaryIndexes).map(gsi => this.getConsumedCapacityAsync(true, params.TableName, gsi.IndexName));
49+
let tableRead = this.getConsumedCapacityAsync(true, params.TableName, null, periodMinutes);
50+
let tableWrite = this.getConsumedCapacityAsync(false, params.TableName, null, periodMinutes);
51+
let gsiReads = this.getArrayOrDefault(params.GlobalSecondaryIndexes).map(gsi => this.getConsumedCapacityAsync(true, params.TableName, gsi.IndexName, periodMinutes));
52+
let gsiWrites = this.getArrayOrDefault(params.GlobalSecondaryIndexes).map(gsi => this.getConsumedCapacityAsync(true, params.TableName, gsi.IndexName, periodMinutes));
5453

5554
// Await on the results
5655
let tableConsumedRead = await tableRead;
@@ -122,10 +121,10 @@ export default class DynamoDB {
122121
return data.data.Datapoints.length === 0 ? 0 : data.data.Datapoints[0].Average;
123122
}
124123

125-
async getConsumedCapacityAsync(isRead, tableName, globalSecondaryIndexName) {
124+
async getConsumedCapacityAsync(isRead, tableName, globalSecondaryIndexName, periodMinutes) {
126125
let EndTime = new Date();
127126
let StartTime = new Date();
128-
StartTime.setTime(EndTime - (60000 * this._timeframeMinutes));
127+
StartTime.setTime(EndTime - (60000 * periodMinutes));
129128
let MetricName = isRead ? 'ConsumedReadCapacityUnits' : 'ConsumedWriteCapacityUnits';
130129
let Dimensions = this.getDimensions(tableName, globalSecondaryIndexName);
131130
let params = {
@@ -134,7 +133,7 @@ export default class DynamoDB {
134133
Dimensions,
135134
StartTime,
136135
EndTime,
137-
Period: (this._timeframeMinutes * 60),
136+
Period: (periodMinutes * 60),
138137
Statistics: [ 'Average' ],
139138
Unit: 'Count'
140139
};

src/RateLimitedDecrement.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
class RateLimitedDecrement {
2+
3+
static isDecrementAllowed(data) {
4+
return this.getNextAllowedDecrementDate(data) <= this.getNowDate();
5+
}
6+
7+
static getNextAllowedDecrementDate(data) {
8+
let lastDecrease = this.parseDate(data.ProvisionedThroughput.LastDecreaseDateTime);
9+
10+
if (data.ProvisionedThroughput.NumberOfDecreasesToday >= 4) {
11+
// Had all the decreases we are allowed
12+
return this.getTomorrowDate();
13+
}
14+
15+
// Get the last decrease or start of day
16+
let lastDecrementDate = this.getLastDecrementDate(lastDecrease);
17+
18+
// Get the next allowed decrement
19+
let lastAllowedDecrementDate = this.getLastAllowedDecrementDate();
20+
let periodMs = lastAllowedDecrementDate.valueOf() - lastDecrementDate.valueOf();
21+
let periodMs2 = periodMs / (5 - data.ProvisionedThroughput.NumberOfDecreasesToday);
22+
let nextDecrementDate = this.getLastDecrementDate(lastDecrease);
23+
nextDecrementDate.setMilliseconds(nextDecrementDate.getMilliseconds() + periodMs2);
24+
return nextDecrementDate;
25+
}
26+
27+
static getNowDate() {
28+
return new Date(Date.now());
29+
}
30+
31+
static getTodayDate() {
32+
let value = this.getNowDate();
33+
value.setHours(0, 0, 0, 0);
34+
return value;
35+
}
36+
37+
static getTomorrowDate() {
38+
let value = this.getTodayDate();
39+
value.setDate(value.getDate() + 1);
40+
return value;
41+
}
42+
43+
static getLastAllowedDecrementDate() {
44+
let value = this.getTodayDate();
45+
value.setHours(23, 30, 0, 0);
46+
return value;
47+
}
48+
49+
static getLastDecrementDate(lastDecrease){
50+
let today = this.getTodayDate();
51+
return lastDecrease < today ? today : new Date(lastDecrease.valueOf());
52+
}
53+
54+
static parseDate(value) {
55+
if (typeof value === 'undefined' || value == null) {
56+
return new Date(-8640000000000000);
57+
}
58+
59+
return Date.parse(value);
60+
}
61+
}
62+
63+
export default RateLimitedDecrement;

src/Throughput.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class Throughput {
2+
static getReadCapacityUtilisationPercent(data) {
3+
return (data.ConsumedThroughput.ReadCapacityUnits / data.ProvisionedThroughput.ReadCapacityUnits) * 100;
4+
}
5+
6+
static getPercentAdjustedReadCapacityUnits(data, max, min) {
7+
let adjustmentUnits = Math.round(data.ProvisionedThroughput.ReadCapacityUnits * (adjustmentPercent / 100));
8+
let newValue = data.ProvisionedThroughput.ReadCapacityUnits + adjustmentUnits;
9+
return Math.max(Math.min(newValue, max), min);
10+
}
11+
12+
static getWriteCapacityUtilisationPercent(data) {
13+
return (data.ConsumedThroughput.WriteCapacityUnits / data.ProvisionedThroughput.WriteCapacityUnits) * 100;
14+
}
15+
16+
static getPercentAdjustedWriteCapacityUnits(data, max, min) {
17+
let adjustmentUnits = Math.round(data.ProvisionedThroughput.WriteCapacityUnits * (adjustmentPercent / 100));
18+
let newValue = data.ProvisionedThroughput.WriteCapacityUnits + adjustmentUnits;
19+
return Math.max(Math.min(newValue, max), min);
20+
}
21+
}
22+
23+
export default Throughput;

0 commit comments

Comments
 (0)