Skip to content

Commit a156a56

Browse files
author
krisjan
committed
Version 1.0
0 parents  commit a156a56

File tree

6 files changed

+153
-0
lines changed

6 files changed

+153
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Link to related article: https://stacktonic.com/article/create-a-user-profile-and-recommender-service-using-big-query-redis-and-gtm-server

gcf-cloudstorage-to-redis/index.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Author Krisjan Oldekamp / Stacktonic.com
3+
4+
* Article https://stacktonic.com/article/create-a-fast-user-profile-and-recommender-service-using-big-query-redis-and-gtm-server
5+
* Based on https://futurice.com/blog/bigquery-to-memorystore
6+
*/
7+
8+
'use strict';
9+
10+
const Redis = require('ioredis');
11+
const {Storage} = require('@google-cloud/storage');
12+
const split = require('split');
13+
14+
const REDISHOST = process.env.REDISHOST || 'localhost';
15+
const REDISPORT = process.env.REDISPORT || 6379;
16+
const EXPIRATION = process.env.EXPIRATION || 259200; // Expiration of records in seconds
17+
const FILE_PATH_OUTBOUND = process.env.FILE_PATH_OUTBOUND || 'outbound';
18+
const FILE_PATH_PROCESSED = process.env.FILE_PATH_PROCESSED || 'processed';
19+
const FILE_PREFIX = process.env.FILE_PREFIX || '';
20+
21+
const redisClient = new Redis({
22+
host: REDISHOST,
23+
port: REDISPORT,
24+
});
25+
26+
/**
27+
* Triggered from a change to a Cloud Storage bucket.
28+
*
29+
* @param {!Object} event Event payload.
30+
* @param {!Object} context Metadata for the event.
31+
*/
32+
exports.loadCloudStorageToRedis = async(info, context) => {
33+
34+
const path = info.name.split('/')
35+
const fileName = path[path.length-1]
36+
37+
if (info.metageneration === '1' && info.name.startsWith(FILE_PATH_OUTBOUND) && fileName.startsWith(FILE_PREFIX)) {
38+
39+
console.log(`New file upload: gs://${info.bucket}/${info.name}`)
40+
41+
const storage = new Storage()
42+
const bucket = storage.bucket(info.bucket);
43+
const file = bucket.file(info.name);
44+
45+
let keysWritten = 0;
46+
47+
try {
48+
49+
// Read file and send to Redis
50+
file.createReadStream()
51+
.on('error', error => reject(error))
52+
.on('response', (response) => {
53+
// connection to GCS opened
54+
}).pipe(split())
55+
.on('data', function (record) {
56+
if (!record || record === "") return;
57+
keysWritten++;
58+
const data = JSON.parse(record);
59+
redisClient.set(data.key, record, 'EX', EXPIRATION);
60+
})
61+
.on('end', () => {
62+
console.log(`Successfully written ${keysWritten} keys to Memcache Redis.`);
63+
64+
// Move file to processed folder
65+
bucket.file(info.name).move(FILE_PATH_PROCESSED + '/' + fileName)
66+
console.log(`File moved to: ${info.bucket}/${FILE_PATH_PROCESSED}/${fileName}`)
67+
68+
resolve();
69+
})
70+
.on('error', error => reject(error));
71+
72+
} catch(e) {
73+
console.log(`Error importing ${fileName} to Redis: ${e}`)
74+
}
75+
}
76+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "gcf-cloudstorage-to-redis",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"@google-cloud/storage": "^5.14.5",
6+
"ioredis": "^4.27.10",
7+
"split": "^1.0.1"
8+
}
9+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{"key": "123", "audience_1": "1", "audience_2": "1", "ab_test_1": "3" }
2+
{"key": "234", "audience_1": "0", "audience_2": "1", "ab_test_2": "2" }
3+
{"key": "456", "audience_1": "1", "audience_2": "0", "ab_test_3": "1" }
4+
{"key": "777", "audience_1": "1", "audience_2": "0", "ab_test_4": "3" }

gcf-http-serve-from-redis/index.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Author Krisjan Oldekamp / Stacktonic.com
3+
4+
* Article https://stacktonic.com/article/
5+
*/
6+
7+
'use strict'
8+
9+
const Redis = require('ioredis');
10+
11+
const REDISHOST = process.env.REDISHOST || 'localhost';
12+
const REDISPORT = process.env.REDISPORT || 6379;
13+
const QUERY_REDIS_KEY = process.env.QUERY_REDIS_KEY || 'id';
14+
const QUERY_SECRET_KEY = process.env.QUERY_SECRET_KEY || 'secret';
15+
const SECRET = process.env.SECRET || '';
16+
17+
const redisClient = new Redis({
18+
host: REDISHOST,
19+
port: REDISPORT,
20+
});
21+
22+
/**
23+
* HTTP function
24+
*
25+
* @param {Object} req Cloud Function request context.
26+
* @param {Object} res Cloud Function response context.
27+
*/
28+
exports.httpServeFromRedis = (req, res) => {
29+
30+
res.set('Access-Control-Allow-Origin', "*");
31+
res.set('Access-Control-Allow-Methods', 'GET');
32+
33+
if (SECRET == '' || (req.query[QUERY_SECRET_KEY] == SECRET)) {
34+
if (typeof req.query[QUERY_REDIS_KEY] != 'undefined') {
35+
const key = req.query[QUERY_REDIS_KEY];
36+
37+
redisClient.get(key, function (err, result) {
38+
if (err) {
39+
console.error(err);
40+
res.status(400).json({ error: 'Error' });
41+
} else {
42+
if (result !== null) {
43+
const data = JSON.parse(result);
44+
res.status(200).json(data);
45+
} else {
46+
res.status(400).json({ error: 'Key not found' });
47+
}
48+
}
49+
});
50+
} else {
51+
res.status(400).json( { error: 'No key specified' } );
52+
}
53+
} else {
54+
res.status(401).json( { error: 'Unauthorized' } );
55+
}
56+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "gcf-http-serve-from-redis",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"ioredis": "^4.27.10"
6+
}
7+
}

0 commit comments

Comments
 (0)