Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: channel data with cache #318

Merged
merged 1 commit into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions apps/web-app/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,5 @@ export default defineNuxtConfig({
pinia: {
storesDirs: ['./stores/**'],
},
routeRules: {
// '/': { swr: true },
// '/catalog/**': { swr: true },
},
compatibilityDate: '2025-02-20',
})
26 changes: 25 additions & 1 deletion packages/core/server/api/channel/index.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { TZDate } from '@date-fns/tz'
import { repository } from '@next-orders/database'
import { getDayIndexByDay, getDayOfWeekByIndex } from './../../../shared/utils/date'

export default defineEventHandler(async () => {
let cacheUpdatedAt: string | null = null

export default defineCachedEventHandler(async () => {
Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a more robust caching solution.

The current implementation using a global variable for cache state has several potential issues:

  1. In a multi-tenant environment, all tenants share the same cache state
  2. Possible memory leaks in long-running servers
  3. Potential race conditions during initialization

Consider these alternatives:

  1. Use a proper caching solution (e.g., Redis)
  2. If keeping the current approach, use a Map to support multiple channels:
-let cacheUpdatedAt: string | null = null
+const channelCacheMap = new Map<string, string>()
  1. Add strict typing:
type ChannelCache = {
  updatedAt: string | null;
}

try {
const { channelId } = useRuntimeConfig()
if (!channelId) {
Expand Down Expand Up @@ -45,4 +47,26 @@ export default defineEventHandler(async () => {
} catch (error) {
throw errorResolver(error)
}
}, {
shouldInvalidateCache,
})

/**
* Determines if the cache should be invalidated based on channel updatedAt
* True if cache should be invalidated, false otherwise
*/
async function shouldInvalidateCache(): Promise<boolean> {
const { channelId } = useRuntimeConfig()

const channel = await repository.channel.find(channelId)
if (!channel) {
return true
}

if (cacheUpdatedAt !== channel.updatedAt) {
cacheUpdatedAt = channel.updatedAt
return true
}

return false
}
Comment on lines +58 to +72
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve cache invalidation robustness and performance.

The current implementation has several areas for improvement:

  1. Each cache validation requires a DB query
  2. Missing error handling for repository calls
  3. No TTL or expiration strategy
  4. No logging of cache invalidation events

Consider this enhanced implementation:

 async function shouldInvalidateCache(): Promise<boolean> {
   const { channelId } = useRuntimeConfig()
+  if (!channelId) {
+    return true
+  }
 
-  const channel = await repository.channel.find(channelId)
+  try {
+    const channel = await repository.channel.find(channelId)
+    if (!channel) {
+      return true
+    }
 
-  if (!channel) {
-    return true
-  }
+    if (cacheUpdatedAt !== channel.updatedAt) {
+      console.log(`Cache invalidated for channel ${channelId}. Previous: ${cacheUpdatedAt}, New: ${channel.updatedAt}`)
+      cacheUpdatedAt = channel.updatedAt
+      return true
+    }
 
-  if (cacheUpdatedAt !== channel.updatedAt) {
-    cacheUpdatedAt = channel.updatedAt
-    return true
+    return false
+  } catch (error) {
+    console.error('Error checking cache validity:', error)
+    return true
   }
- 
-  return false
 }

Consider implementing a more sophisticated caching strategy:

  1. Add TTL to prevent stale cache in case updatedAt isn't changed
  2. Use cache warming to prevent cold starts
  3. Implement circuit breaker for DB calls
📝 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 shouldInvalidateCache(): Promise<boolean> {
const { channelId } = useRuntimeConfig()
const channel = await repository.channel.find(channelId)
if (!channel) {
return true
}
if (cacheUpdatedAt !== channel.updatedAt) {
cacheUpdatedAt = channel.updatedAt
return true
}
return false
}
async function shouldInvalidateCache(): Promise<boolean> {
const { channelId } = useRuntimeConfig()
if (!channelId) {
return true
}
try {
const channel = await repository.channel.find(channelId)
if (!channel) {
return true
}
if (cacheUpdatedAt !== channel.updatedAt) {
console.log(`Cache invalidated for channel ${channelId}. Previous: ${cacheUpdatedAt}, New: ${channel.updatedAt}`)
cacheUpdatedAt = channel.updatedAt
return true
}
return false
} catch (error) {
console.error('Error checking cache validity:', error)
return true
}
}