Skip to content

Conversation

nickventon
Copy link
Collaborator

@nickventon nickventon commented Sep 5, 2025

  • Added Redis client setup in src/redisClient.ts for the classifier service
  • Included a test script src/redisTest.ts to verify connection with the local Redis instance
  • Documented the setup and added basic error handling for connection
  • Confirmed successful communication with Redis using Dockerized local server

Summary by CodeRabbit

  • New Features
    • Added a resilient external cache connection layer with automatic retries and error logging to improve stability.
  • Tests
    • Introduced a connectivity test script to validate cache availability and basic operations.
  • Chores
    • Set up TypeScript configuration with strict mode and CommonJS module output.
    • Added project dependencies and scripts to support environment configuration and development tooling.

Copy link

coderabbitai bot commented Sep 5, 2025

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/init-classifier-redis

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nickventon
Copy link
Collaborator Author

Hello @tasin2610 Kindly check my PR

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
package.json (1)

1-10: Add basic metadata and scripts to build/run the Redis test

Project lacks name/license/scripts/engines. Add them to enable compile + test execution and ensure Node compatibility.

-{
-  "dependencies": {
-    "dotenv": "^17.2.2",
-    "redis": "^5.8.2"
-  },
-  "devDependencies": {
-    "@types/node": "^24.3.1",
-    "typescript": "^5.9.2"
-  }
-}
+{
+  "name": "classifier-service",
+  "version": "0.1.0",
+  "private": true,
+  "license": "MIT",
+  "engines": { "node": ">=18.18.0" },
+  "scripts": {
+    "build": "tsc -p tsconfig.json",
+    "start:redis-test": "node dist/src/redisTest.js"
+  },
+  "dependencies": {
+    "dotenv": "^17.2.2",
+    "redis": "^5.8.2"
+  },
+  "devDependencies": {
+    "@types/node": "^24.3.1",
+    "typescript": "^5.9.2"
+  }
+}
tsconfig.json (1)

1-9: Tighten TS config for Node service builds

Set rootDir to keep clean outputs; align target with modern Node; include Node types; add sensible defaults.

 {
   "compilerOptions": {
-    "module": "CommonJS",
-    "target": "ES6",
-    "outDir": "dist",
-    "esModuleInterop": true,
-    "strict": true
+    "module": "CommonJS",
+    "moduleResolution": "node",
+    "target": "ES2020",
+    "lib": ["ES2020"],
+    "rootDir": "src",
+    "outDir": "dist",
+    "types": ["node"],
+    "esModuleInterop": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "resolveJsonModule": true
   }
 }
src/redisTest.ts (1)

1-1: Remove unused import to avoid noise

getRedisClient isn’t used here.

-import { initRedis, getRedisClient } from "./redisClient";
+import { initRedis } from "./redisClient";
src/redisClient.ts (3)

6-8: Avoid brittle string replace for localhost; parse and normalize URL

Direct .replace("localhost", "127.0.0.1") can mangle credentials or non-host parts. Parse with URL and only adjust hostname.

-// Prefer IPv4 loopback by default to avoid ::1/IPv6 resolution issues on some systems
-const REDIS_URL = (process.env.REDIS_URL || "redis://127.0.0.1:6379").replace("localhost", "127.0.0.1");
+// Prefer IPv4 loopback by default to avoid ::1/IPv6 resolution issues on some systems
+const buildRedisUrl = (raw: string): string => {
+  const fallback = "redis://127.0.0.1:6379";
+  const val = raw && raw.trim() ? raw : fallback;
+  try {
+    const u = new URL(val);
+    if (u.hostname === "localhost") u.hostname = "127.0.0.1";
+    return u.toString();
+  } catch {
+    return fallback;
+  }
+};
+const REDIS_URL = buildRedisUrl(process.env.REDIS_URL ?? "");

43-46: Guard against returning a closed client

If the client was initialized but later closed, getRedisClient() should throw.

 export function getRedisClient(): RedisClientType {
-  if (!client) throw new Error("Redis not initialized. Call initRedis() first.");
-  return client;
+  if (!client?.isOpen) {
+    throw new Error("Redis not initialized or not open. Call initRedis() first.");
+  }
+  return client;
 }

41-46: Provide a clean shutdown helper for services

Expose stopRedis() for graceful service termination.

   throw new Error("Redis connect failed");
 }
 
 export function getRedisClient(): RedisClientType {
   if (!client) throw new Error("Redis not initialized. Call initRedis() first.");
   return client;
 }
+
+export async function stopRedis(): Promise<void> {
+  if (client?.isOpen) {
+    try {
+      await client.quit();
+    } finally {
+      client = null;
+    }
+  }
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7583d53 and b560321.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • src/redisClient.ts (1 hunks)
  • src/redisTest.ts (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/redisTest.ts (1)
src/redisClient.ts (1)
  • initRedis (11-41)
🔇 Additional comments (1)
src/redisClient.ts (1)

16-18: Do not log secrets: mask credentials in URLs

console.log("Redis connected:", REDIS_URL) can leak username/password. Mask before logging.

-  client.on("error", (err: unknown) => {
-    console.error("Redis Client Error:", err);
-  });
+  client.on("error", (err: unknown) => {
+    console.error("Redis Client Error:", err);
+  });
+
+  const maskUrl = (u: string): string => {
+    try {
+      const parsed = new URL(u);
+      if (parsed.password) parsed.password = "****";
+      if (parsed.username) parsed.username = "****";
+      return parsed.toString();
+    } catch {
+      return u.replace(/\/\/[^@]*@/, "//****:****@");
+    }
+  };

Likely an incorrect or invalid review comment.

Comment on lines +11 to +15
export async function initRedis(): Promise<RedisClientType> {
if (client && client.isOpen) return client;

client = createClient({ url: REDIS_URL });

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent duplicate concurrent connections and set connection timeout

If two callers invoke initRedis() concurrently, you can create two clients. Also, set a connectTimeout and IPv4 family to ensure timely failure.

-let client: RedisClientType | null = null;
+let client: RedisClientType | null = null;
+let initPromise: Promise<RedisClientType> | null = null;
@@
 export async function initRedis(): Promise<RedisClientType> {
-  if (client && client.isOpen) return client;
+  if (client?.isOpen) return client;
+  if (initPromise) return initPromise;
 
-  client = createClient({ url: REDIS_URL });
+  client = createClient({
+    url: REDIS_URL,
+    socket: {
+      // Ensure connect() rejects promptly on unreachable hosts
+      connectTimeout: 5_000,
+      family: 4
+    }
+  });
@@
-  for (let attempt = 1; attempt <= maxRetries; attempt++) {
+  initPromise = (async () => {
+    for (let attempt = 1; attempt <= maxRetries; attempt++) {
       try {
         await client.connect();
-      console.log("Redis connected:", REDIS_URL);
+        console.log("Redis connected:", maskUrl(REDIS_URL));
         return client;
       } catch (err) {
         console.error(`Redis connect attempt ${attempt} failed:`, err);
         if (attempt === maxRetries) {
-        client = null;
-        console.error("All Redis connection attempts failed.");
-        throw err;
+          // Ensure resources are closed before dropping the reference
+          try { await client.disconnect(); } catch {}
+          client = null;
+          console.error("All Redis connection attempts failed.");
+          throw err;
         }
         const delay = baseDelayMs * 2 ** (attempt - 1);
         await new Promise((res) => setTimeout(res, delay));
       }
-  }
-  // unreachable, but satisfy TypeScript
-  throw new Error("Redis connect failed");
+    }
+    // Unreachable
+    throw new Error("Redis connect failed");
+  })();
+  try {
+    return await initPromise;
+  } finally {
+    initPromise = null;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function initRedis(): Promise<RedisClientType> {
if (client && client.isOpen) return client;
client = createClient({ url: REDIS_URL });
let client: RedisClientType | null = null;
let initPromise: Promise<RedisClientType> | null = null;
export async function initRedis(): Promise<RedisClientType> {
if (client?.isOpen) return client;
if (initPromise) return initPromise;
client = createClient({
url: REDIS_URL,
socket: {
// Ensure connect() rejects promptly on unreachable hosts
connectTimeout: 5_000,
family: 4
}
});
initPromise = (async () => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await client.connect();
console.log("Redis connected:", maskUrl(REDIS_URL));
return client;
} catch (err) {
console.error(`Redis connect attempt ${attempt} failed:`, err);
if (attempt === maxRetries) {
// Ensure resources are closed before dropping the reference
try { await client.disconnect(); } catch {}
client = null;
console.error("All Redis connection attempts failed.");
throw err;
}
const delay = baseDelayMs * 2 ** (attempt - 1);
await new Promise((res) => setTimeout(res, delay));
}
}
// Unreachable
throw new Error("Redis connect failed");
})();
try {
return await initPromise;
} finally {
initPromise = null;
}
}
🤖 Prompt for AI Agents
In src/redisClient.ts around lines 11-15, avoid creating duplicate clients on
concurrent initRedis() calls and add a connection timeout + IPv4 family:
introduce a module-scoped "connectingPromise" (Promise<RedisClientType> | null)
so initRedis first returns existing open client, then if connectingPromise
exists await and return it, otherwise set connectingPromise before creating the
client, pass socket options to createClient e.g. { url: REDIS_URL, socket: {
connectTimeout: <reasonable-ms>, family: 4 } }, await client.connect(), assign
the resolved client to the module-scoped client, clear connectingPromise in
finally or on error (re-throw error), and return the singleton client.

Comment on lines +3 to +12
async function testRedis() {
const client = await initRedis();

const pong = await client.ping();
console.log("Ping Response:", pong); // Output: "PONG" means connection success

await client.disconnect();
}

testRedis();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make the script fail-fast and exit cleanly; prefer QUIT over disconnect

Wrap in try/catch, set exit code, and use quit() for graceful shutdown.

 async function testRedis() {
-  const client = await initRedis();
-
-  const pong = await client.ping();
-  console.log("Ping Response:", pong); // Output: "PONG" means connection success
-
-  await client.disconnect();
+  const client = await initRedis();
+  try {
+    const pong = await client.ping();
+    console.log("Ping Response:", pong); // "PONG" means connection success
+  } finally {
+    await client.quit();
+  }
 }
 
-testRedis();
+testRedis().then(() => process.exit(0)).catch((err) => {
+  console.error("Redis test failed:", err);
+  process.exit(1);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function testRedis() {
const client = await initRedis();
const pong = await client.ping();
console.log("Ping Response:", pong); // Output: "PONG" means connection success
await client.disconnect();
}
testRedis();
async function testRedis() {
const client = await initRedis();
try {
const pong = await client.ping();
console.log("Ping Response:", pong); // "PONG" means connection success
} finally {
await client.quit();
}
}
testRedis()
.then(() => process.exit(0))
.catch((err) => {
console.error("Redis test failed:", err);
process.exit(1);
});
🤖 Prompt for AI Agents
In src/redisTest.ts around lines 3 to 12, the testRedis script should be made
fail-fast and shut down gracefully: wrap the logic in an async
try/catch/finally, create the Redis client before the try if needed, in try
await client.ping() and log the response, in catch log the error and set
process.exitCode = 1, and in finally if client exists await client.quit() (not
disconnect) to ensure graceful QUIT; after the async test call, await completion
and call process.exit(process.exitCode || 0) if you want an explicit exit.

@tasin2610 tasin2610 merged commit c42b6f8 into main Sep 6, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants