LibreChat/deploy/aws-sam/scripts/flush-redis-cache.sh

202 lines
5.7 KiB
Bash
Raw Normal View History

#!/bin/bash
# Script to flush Redis cache by running a one-off ECS task
# Usage: ./scripts/flush-redis-cache.sh
set -e
# Load configuration
if [ ! -f .librechat-deploy-config ]; then
echo "Error: .librechat-deploy-config not found"
exit 1
fi
source .librechat-deploy-config
# Set variables
CLUSTER_NAME="${STACK_NAME}-cluster"
TASK_FAMILY="${STACK_NAME}-task"
REGION="${REGION:-us-east-1}"
echo "=========================================="
echo "Flushing Redis Cache"
echo "Stack: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Get VPC configuration from the existing service
echo "Getting network configuration from existing service..."
SERVICE_INFO=$(aws ecs describe-services \
--cluster "$CLUSTER_NAME" \
--services "${STACK_NAME}-service" \
--region "$REGION" \
--query 'services[0].networkConfiguration.awsvpcConfiguration' \
--output json)
SUBNETS=$(echo "$SERVICE_INFO" | jq -r '.subnets | join(",")')
SECURITY_GROUPS=$(echo "$SERVICE_INFO" | jq -r '.securityGroups | join(",")')
echo "Subnets: $SUBNETS"
echo "Security Groups: $SECURITY_GROUPS"
# Get the task definition
echo "Getting task definition..."
TASK_DEF=$(aws ecs describe-task-definition \
--task-definition "$TASK_FAMILY" \
--region "$REGION" \
--query 'taskDefinition.taskDefinitionArn' \
--output text)
echo "Task Definition: $TASK_DEF"
# Run the one-off task
echo "Starting ECS task to flush Redis cache..."
# Inline Node.js script to flush Redis cache
FLUSH_SCRIPT='
const IoRedis = require("ioredis");
const isEnabled = (value) => value === "true" || value === true;
async function flushRedis() {
try {
console.log("🔍 Connecting to Redis...");
const urls = (process.env.REDIS_URI || "").split(",").map((uri) => new URL(uri));
const username = urls[0]?.username || process.env.REDIS_USERNAME;
const password = urls[0]?.password || process.env.REDIS_PASSWORD;
const redisOptions = {
username: username,
password: password,
connectTimeout: 10000,
maxRetriesPerRequest: 3,
enableOfflineQueue: true,
lazyConnect: false,
};
const useCluster = urls.length > 1 || isEnabled(process.env.USE_REDIS_CLUSTER);
let redis;
if (useCluster) {
const clusterOptions = {
redisOptions,
enableOfflineQueue: true,
};
if (isEnabled(process.env.REDIS_USE_ALTERNATIVE_DNS_LOOKUP)) {
clusterOptions.dnsLookup = (address, callback) => callback(null, address);
}
redis = new IoRedis.Cluster(
urls.map((url) => ({ host: url.hostname, port: parseInt(url.port, 10) || 6379 })),
clusterOptions,
);
} else {
redis = new IoRedis(process.env.REDIS_URI, redisOptions);
}
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("Connection timeout")), 10000);
redis.once("ready", () => { clearTimeout(timeout); resolve(); });
redis.once("error", (err) => { clearTimeout(timeout); reject(err); });
});
console.log("✅ Connected to Redis");
let keyCount = 0;
try {
if (useCluster) {
const nodes = redis.nodes("master");
for (const node of nodes) {
const keys = await node.keys("*");
keyCount += keys.length;
}
} else {
const keys = await redis.keys("*");
keyCount = keys.length;
}
} catch (_error) {}
if (useCluster) {
const nodes = redis.nodes("master");
await Promise.all(nodes.map((node) => node.flushdb()));
console.log(`✅ Redis cluster cache flushed successfully (${nodes.length} master nodes)`);
} else {
await redis.flushdb();
console.log("✅ Redis cache flushed successfully");
}
if (keyCount > 0) {
console.log(` Deleted ${keyCount} keys`);
}
await redis.disconnect();
console.log("⚠️ Note: All users will need to re-authenticate");
process.exit(0);
} catch (error) {
console.error("❌ Error flushing Redis cache:", error.message);
process.exit(1);
}
}
flushRedis();
'
SHELL_CMD="cd /app && node -e '$FLUSH_SCRIPT'"
# Build the overrides JSON using jq for proper escaping
OVERRIDES=$(jq -n \
--arg cmd "$SHELL_CMD" \
'{
containerOverrides: [{
name: "librechat",
command: ["sh", "-c", $cmd]
}]
}')
echo "Running command in container..."
TASK_ARN=$(aws ecs run-task \
--cluster "$CLUSTER_NAME" \
--task-definition "$TASK_DEF" \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SECURITY_GROUPS],assignPublicIp=DISABLED}" \
--overrides "$OVERRIDES" \
--region "$REGION" \
--query 'tasks[0].taskArn' \
--output text)
echo "Task started: $TASK_ARN"
echo ""
echo "Waiting for task to complete..."
echo "You can monitor the task with:"
echo " aws ecs describe-tasks --cluster $CLUSTER_NAME --tasks $TASK_ARN --region $REGION"
echo ""
echo "Or view logs in CloudWatch Logs:"
echo " Log Group: /ecs/${STACK_NAME}-task"
echo ""
# Wait for task to complete
aws ecs wait tasks-stopped \
--cluster "$CLUSTER_NAME" \
--tasks "$TASK_ARN" \
--region "$REGION"
# Check task exit code
EXIT_CODE=$(aws ecs describe-tasks \
--cluster "$CLUSTER_NAME" \
--tasks "$TASK_ARN" \
--region "$REGION" \
--query 'tasks[0].containers[0].exitCode' \
--output text)
if [ "$EXIT_CODE" = "0" ]; then
echo "✅ Success! Redis cache has been flushed."
echo ""
echo "⚠️ Note: All users will need to re-authenticate."
else
echo "❌ Task failed with exit code: $EXIT_CODE"
echo "Check CloudWatch Logs for details:"
echo " aws logs tail /ecs/${STACK_NAME}-task --follow --region $REGION"
exit 1
fi