Reputation: 1179
I'm trying getting a socket connect error trying to reach my AWS Serverless Redis Cache. I defined both the lambda and the redis cache in the same VPC and the same subnets. This code worked when I did the AWS self managed Redis offering. Something seems different about the serverless for me.
Below is my Lambda code, but I believe this is much more related to AWS not allowing the connection from the lambda to the Redis cluster. What things might be blocking this?
My lambda Terraform:
resource "aws_lambda_function" "lambda" {
function_name = "rfl-${var.deployment_environment}-lambda"
role = aws_iam_role.my_role.arn
timeout = 180
handler = "lambda/preload_cache.handler"
runtime = "nodejs18.x"
filename = "lambdas/target/lambda.zip"
source_code_hash = filebase64sha256("lambdas/target/preload-cache-lambda.zip")
environment {
variables = {
MY_TOPIC_ARN = aws_sns_topic.messages_topic.arn
CACHE_URL = "redis://${awscc_elasticache_serverless_cache.redis_cluster.endpoint.address}:${awscc_elasticache_serverless_cache.redis_cluster.endpoint.port}"
LOG_LEVEL = var.log_level
}
}
vpc_config {
subnet_ids = var.metadata_subnet_ids
security_group_ids = [aws_security_group.gather_barcode_data_security_group.id]
}
depends_on = [aws_sns_topic.messages_topic, awscc_elasticache_serverless_cache.redis_cluster,aws_iam_role.my_role]
publish = true
}
resource "awscc_elasticache_serverless_cache" "redis_cluster" {
serverless_cache_name = "rfl-${var.deployment_environment}-my-elasticache-serverless-cache"
description = "ElastiCache Redis Serverless"
engine = "redis"
major_engine_version = "7"
security_group_ids = [aws_security_group.redis_security_group.id]
subnet_ids = var.metadata_subnet_ids
depends_on = [aws_security_group.redis_security_group]
}
An abbreviated version of my lambda:
import { SNS } from 'aws-sdk';
import { APIGatewayProxyHandler, APIGatewayProxyEvent } from 'aws-lambda';
import { loadMetaDataPanels, openCache, closeCache } from './metadata';
import { RedisClientType } from 'redis';
import fs from 'fs';
import path from 'path';
import { request } from 'http';
import { match } from 'assert';
import { endianness } from 'os';
// Function to read JSON file and parse it to an object
export function readJsonFile(fileName: string): any {
try {
const filePath = path.join(__dirname, fileName);
const fileContents = fs.readFileSync(filePath, 'utf8');
return JSON.parse(fileContents);
} catch (err) {
console.error('Error reading file:', err);
return null;
}
}
let redisClient : RedisClientType | null = null;
export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEvent) => {
let cacheUrl = process.env.CACHE_URL;
let metaDataConnection = await openCache(cacheUrl as string);
await loadMetaDataPanels(metaDataConnection, readJsonFile('./mydata.json'));
await closeCache(metaDataConnection);
return {
statusCode: 200,
body: JSON.stringify({ error: 'No data provided or data is empty' })
};
};
export async function openRedisConnection(url : string): Promise<RedisClientType> {
return new Promise<RedisClientType>(async (resolve,reject)=>{
try {
let redisClient : RedisClientType = createClient({
url: url
});
await redisClient.connect();
resolve(redisClient);
} catch(error) {
reject(error);
}
});
}
type RedisReturnFormat = {
"data": string;
};
export async function openCache(url : string) : Promise<MetaDataConnection> {
return {
connection : await openRedisConnection(url),
url: url
}
}
export async function loadMetaDataPanels(metaDataConnection : MetaDataConnection, metaDataPanelsIn: MetaDataPanels): Promise<MetaDataConnection> {
try {
const multi = (metaDataConnection.connection as RedisClientType).multi();
for (const [panelCode, metaDataPanel] of Object.entries(metaDataPanelsIn)) {
let metaDataPanelString : string = JSON.stringify(metaDataPanel);
multi.hSet(panelCode as string, 'data', metaDataPanelString); // Storing each MetaDataPanelEntryType as a hash
}
let redisCommandRawReply = await multi.exec();
return metaDataConnection;
} catch(error) {
console.log('loadMetaDataPanels: Redis error: '+error);
throw error;
};
}
export async function getMetaDataPanels(metaDataConnection : MetaDataConnection, panelCodes: string[]) : Promise<MetaDataPanels> {
return new Promise<MetaDataPanels >((resolve,reject)=>{
/* Build luascript and insert empty JSON if key is not found */
const luaScript : string = `
local results = {}
for i, key in ipairs(KEYS) do
local value = redis.call('HGET', key, 'data')
if value then
table.insert(results, value)
else
table.insert(results, '{}')
end
end
return results
`;
try {
const result : MetaDataPanels = {};
(async () =>{
const evalOptions = {
keys: panelCodes,
arguments: []
};
(async()=>{
let value = await (metaDataConnection.connection as RedisClientType).get('1113');
});
const metaDataPanelsArray : string[] = await (metaDataConnection.connection as RedisClientType).eval(luaScript,evalOptions ) as string[];
metaDataPanelsArray.forEach((json:string,index:number)=>{
const panelCode = panelCodes[index];
const redisValueReturned : RedisReturnFormat = JSON.parse(json);
result[panelCode] = JSON.parse(json);
});
resolve(result);
})();
} catch (error) {
reject(error);
}
})
}
export async function closeCache(metaDataConnection : MetaDataConnection) {
console.log('Attempting to close cache');
if(metaDataConnection != null && metaDataConnection.connection != null) {
console.log('Attempting to close valid cache');
return (metaDataConnection.connection as RedisClientType).quit();
}
}
export async function getMetaDataPanelEntryType(metaDataConnection : MetaDataConnection, panelCode: string): Promise<MetaDataPanelEntryType | null> {
try {
const metaData = await (metaDataConnection.connection as RedisClientType).hGetAll(panelCode);
if (Object.keys(metaData).length === 0) {
return null;
}
return metaData as unknown as MetaDataPanelEntryType;
} catch (error) {
console.error('Error fetching MetaDataPanelEntryType from Redis:', error);
return null;
};
}
Upvotes: 1
Views: 801
Reputation: 1179
If this helps anyone else, the problem was tls needed to be specified. I changed the createClient to something like:
let redisClient : RedisClientType = createClient({
socket: {hostname: 'myhost', port: 6379, tls: true}
});
Upvotes: 1