mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-07 08:25:23 +02:00
202 lines
5.7 KiB
Bash
202 lines
5.7 KiB
Bash
|
|
#!/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
|