Skip to content

Commit f275470

Browse files
authored
chore: use refresh start time to refresh base view (#1457)
1 parent ce3f3c4 commit f275470

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

src/analytics/lambdas/refresh-materialized-views-workflow/refresh-basic-view.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export interface RefreshBasicViewEvent {
2626
appId: string;
2727
timezone: string;
2828
};
29+
originalInput: {
30+
refreshStartTime: string;
31+
};
2932
}
3033

3134
/**
@@ -51,14 +54,21 @@ export const handler = async (event: RefreshBasicViewEvent) => {
5154

5255
const sqlStatements: string[] = [];
5356
const timezoneWithAppId = event.timezoneWithAppId;
57+
const originalInput = event.originalInput;
5458
const viewName = event.view.name;
5559

56-
const dataFreshnessInHour = process.env.DATA_REFRESHNESS_IN_HOUR!;
57-
5860
try {
5961
let queryId : string | undefined;
6062
const type = event.view.type;
6163
if (type === 'custom-mv') {
64+
let dataFreshnessInHour = process.env.DATA_REFRESHNESS_IN_HOUR!;
65+
if (originalInput.refreshStartTime) {
66+
let dataFreshnessInHourNumber = calculateHoursAgo(originalInput.refreshStartTime);
67+
if (dataFreshnessInHourNumber > 0) {
68+
dataFreshnessInHour = dataFreshnessInHourNumber.toString();
69+
}
70+
}
71+
logger.info('dataFreshnessInHour', { dataFreshnessInHour });
6272
sqlStatements.push(`CALL ${timezoneWithAppId.appId}.${viewName}(NULL, NULL, ${dataFreshnessInHour});`);
6373
} else {
6474
sqlStatements.push(`REFRESH MATERIALIZED VIEW ${timezoneWithAppId.appId}.${viewName};`);
@@ -79,4 +89,11 @@ export const handler = async (event: RefreshBasicViewEvent) => {
7989
logger.error('Error when refresh mv:', { err });
8090
throw err;
8191
}
82-
};
92+
};
93+
94+
function calculateHoursAgo(refreshStartTime: string) {
95+
const currentDate = new Date();
96+
const diffInMilliseconds = currentDate.getTime() - parseInt(refreshStartTime);
97+
const diffInHours = Math.floor(diffInMilliseconds / (1000 * 60 * 60) ) + 1;
98+
return diffInHours;
99+
}

src/analytics/private/refresh-materialized-views-workflow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export class RefreshMaterializedViewsWorkflow extends Construct {
9898
payload: TaskInput.fromObject({
9999
'view.$': '$.view',
100100
'timezoneWithAppId.$': '$.timezoneWithAppId',
101+
'originalInput.$': '$$.Execution.Input',
101102
}),
102103
outputPath: '$.Payload',
103104
});

test/analytics/analytics-on-redshift/lambda/refresh-materialized-views-workflow/refresh-basic-view.test.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ const refreshBasicViewEvent: RefreshBasicViewEvent = {
2626
},
2727
timezoneWithAppId: {
2828
appId: 'app1',
29-
timezone: 'America/Noronha',
29+
timezone: 'UTC',
30+
},
31+
originalInput: {
32+
refreshStartTime: '',
3033
},
3134
};
3235

@@ -62,8 +65,86 @@ describe('Lambda - do refresh job in Redshift Serverless', () => {
6265
});
6366
});
6467

68+
test('Executed Redshift refresh custom-mv command', async () => {
69+
const exeuteId = 'Id-1';
70+
refreshBasicViewEvent.view.type = 'custom-mv';
71+
refreshBasicViewEvent.originalInput = {
72+
refreshStartTime: '1715470905000',
73+
};
74+
jest
75+
.useFakeTimers()
76+
.setSystemTime(1715490905000);
77+
const dataFreshnessInHour = 6;
78+
redshiftDataMock.on(ExecuteStatementCommand).resolvesOnce({ Id: exeuteId });
79+
const resp = await handler(refreshBasicViewEvent);
80+
expect(resp).toEqual({
81+
detail: {
82+
viewName: refreshBasicViewEvent.view.name,
83+
queryId: exeuteId,
84+
},
85+
timezoneWithAppId: refreshBasicViewEvent.timezoneWithAppId,
86+
});
87+
expect(redshiftDataMock).toHaveReceivedCommandWith(ExecuteStatementCommand, {
88+
WorkgroupName: workGroupName,
89+
Sql: `CALL ${refreshBasicViewEvent.timezoneWithAppId.appId}.${refreshBasicViewEvent.view.name}(NULL, NULL, ${dataFreshnessInHour});`,
90+
Database: expect.any(String),
91+
});
92+
jest.useRealTimers();
93+
});
94+
95+
test('Executed Redshift refresh custom-mv command with RefreshStartTime bigger than current', async () => {
96+
const exeuteId = 'Id-1';
97+
refreshBasicViewEvent.view.type = 'custom-mv';
98+
refreshBasicViewEvent.originalInput = {
99+
refreshStartTime: '1715470905000',
100+
};
101+
jest
102+
.useFakeTimers()
103+
.setSystemTime(1715450905000);
104+
const dataFreshnessInHour = 72;
105+
redshiftDataMock.on(ExecuteStatementCommand).resolvesOnce({ Id: exeuteId });
106+
const resp = await handler(refreshBasicViewEvent);
107+
expect(resp).toEqual({
108+
detail: {
109+
viewName: refreshBasicViewEvent.view.name,
110+
queryId: exeuteId,
111+
},
112+
timezoneWithAppId: refreshBasicViewEvent.timezoneWithAppId,
113+
});
114+
expect(redshiftDataMock).toHaveReceivedCommandWith(ExecuteStatementCommand, {
115+
WorkgroupName: workGroupName,
116+
Sql: `CALL ${refreshBasicViewEvent.timezoneWithAppId.appId}.${refreshBasicViewEvent.view.name}(NULL, NULL, ${dataFreshnessInHour});`,
117+
Database: expect.any(String),
118+
});
119+
jest.useRealTimers();
120+
});
121+
122+
test('Executed Redshift refresh custom-mv command without input refreshStartTime', async () => {
123+
const exeuteId = 'Id-1';
124+
refreshBasicViewEvent.view.type = 'custom-mv';
125+
refreshBasicViewEvent.originalInput = {
126+
refreshStartTime: '',
127+
};
128+
const dataFreshnessInHour = 72;
129+
redshiftDataMock.on(ExecuteStatementCommand).resolvesOnce({ Id: exeuteId });
130+
const resp = await handler(refreshBasicViewEvent);
131+
expect(resp).toEqual({
132+
detail: {
133+
viewName: refreshBasicViewEvent.view.name,
134+
queryId: exeuteId,
135+
},
136+
timezoneWithAppId: refreshBasicViewEvent.timezoneWithAppId,
137+
});
138+
expect(redshiftDataMock).toHaveReceivedCommandWith(ExecuteStatementCommand, {
139+
WorkgroupName: workGroupName,
140+
Sql: `CALL ${refreshBasicViewEvent.timezoneWithAppId.appId}.${refreshBasicViewEvent.view.name}(NULL, NULL, ${dataFreshnessInHour});`,
141+
Database: expect.any(String),
142+
});
143+
});
144+
65145
test('Execute command error in Redshift when doing refresh', async () => {
66146
redshiftDataMock.on(ExecuteStatementCommand).rejects();
147+
refreshBasicViewEvent.view.type = 'basic';
67148
try {
68149
await handler(refreshBasicViewEvent);
69150
fail('The error in executing statement of Redshift data was caught');
@@ -79,7 +160,6 @@ describe('Lambda - do refresh job in Redshift Serverless', () => {
79160
redshiftDataMock.on(ExecuteStatementCommand)
80161
.rejectsOnce()
81162
.resolves({ Id: exeuteId });
82-
83163
const resp = await handler(refreshBasicViewEvent);
84164
expect(resp).toEqual({
85165
detail: {

0 commit comments

Comments
 (0)