You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用node-cache结合Firebase后端时,设置缓存后无法获取数据

Node-Cache 设置成功但获取始终返回 Undefined(Firebase 后端场景)

我正在基于 Firebase 构建自研后端 API,计划用 node-cache 实现缓存功能,但执行缓存设置(返回真值)后,获取缓存时始终返回 undefined。

缓存类代码

import {injectable} from "inversify";
import * as NodeCache from "node-cache";
import {SearchDataFile} from "../../searching/domain/search_domian";

@injectable()
class Caching {
  private myCache:NodeCache;
  constructor() {
    this.myCache = new NodeCache({stdTTL: 7000, checkperiod: 120});
  }
  public get(key: string): SearchDataFile | undefined {
    return this.myCache.get(key);
  }

  public checkKeyAvailable(keyValue: string): boolean {
    return this.myCache.has(keyValue);
  }
  public set(key: string, data: SearchDataFile): boolean {
    return this.myCache.set(key, data, 3600);
  }
  public delete(key: string): string[] | undefined {
    return this.myCache.take(key);
  }
}

export default Caching;

缓存使用场景代码

/* eslint-disable max-len */
/* eslint-disable object-curly-spacing */
import {FirebaseUser} from "../../authentication/domain/userEntity";
import {SearchDataFile} from "../domain/search_domian";
import * as admin from "firebase-admin";
import {SearchServicesRepository} from "./crednetials/searchRepository";
import { injectable, inject } from "inversify";
import Caching from "../../core/application/cache";
import * as logger from "firebase-functions/logger";

@injectable()
export class SearchServiceImplementation implements SearchServicesRepository {
  constructor(@inject(Caching) private caching: Caching ) {}

  async getAlldata(): Promise<SearchDataFile> {
    const valueCheck = this.caching.get("allData");
    logger.debug(`the checking ==== ${valueCheck}`);
    if (valueCheck != undefined) {
      logger.debug(`the checking after check ==== ${valueCheck}`);
      //  const cachedDataFromDB:SearchDataFile = this.caching.get("allData") as SearchDataFile;
      return valueCheck;
    }
    const usersData = await admin.firestore().collection("users").get();
    const firbaseusers:FirebaseUser[] = usersData.docs.map((users)=> {
      const userData = users.data() as FirebaseUser;
      return userData;
    });
    const data:SearchDataFile = {
      users: firbaseusers,
      community: [],
      library: [],
      news: [],
      forum: [],
    };
    const value =this.caching.set("allData", data);
    logger.debug(`getAllData ${value}`);
    return data;
  }


  async search(searchTerm: string): Promise<SearchDataFile> {
    const valueCheck = this.caching.checkKeyAvailable("allData");
    logger.debug(`the checking if data is store in cache ${valueCheck}`);
    if (valueCheck) {
      const cachedDataFromDB:SearchDataFile = this.caching.get("allData") as SearchDataFile;
      logger.debug(`the data is store in cache ${cachedDataFromDB}`);
      const filteredUsers = cachedDataFromDB.users.filter((user) =>
        user.username.toLowerCase().includes(searchTerm.toLowerCase())
      );
      return {
        users: filteredUsers,
        community: [],
        library: [],
        news: [],
        forum: [],
      };
    }
    const usersData = await admin.firestore().collection("users").get();
    const firbaseusers:FirebaseUser[] = usersData.docs.map((users)=> {
      const userData = users.data() as FirebaseUser;
      return userData;
    });
    const filteredUsers = firbaseusers.filter((user) =>
      user.username.toLowerCase().includes(searchTerm.toLowerCase())
    );
    return {
      users: filteredUsers,
      community: [],
      library: [],
      news: [],
      forum: [],
    };
  }
}

调用代码

/* eslint-disable max-len */
import {Request, Response} from "express";
import {myContainer} from "../../core/inversify_config";
import {TYPES} from "../../core/types";
import {SearchServicesRepository} from "../infastructure/crednetials/searchRepository";

export async function getAllFromDB(req: Request, res: Response,) {
  try {
    const getAllData = myContainer.get<SearchServicesRepository>(TYPES.SearchServicesRepository);
    const data = await getAllData.getAlldata();
    return res.status(200).send({message: "Succesfull", data});
  } catch (error) {
    return res.status(400).send(error);
  }
}

相关日志信息

Google Cloud Console 日志显示:

  • 首次调用 getAlldata 时,valueCheck 为 undefined,随后缓存设置返回 true
  • 后续调用中,valueCheck 仍为 undefined,checkKeyAvailable 返回 false

问题排查与解决方案

1. 修正 Inversify 实例作用域

Caching 类默认是瞬态实例(每次请求创建新对象),导致缓存无法跨请求共享。需在 Inversify 配置中将其注册为单例:

// 在 inversify_config.ts 中
import Caching from './path/to/Caching';

myContainer.bind<Caching>(Caching).toSelf().inSingletonScope();

2. 验证数据序列化兼容性

node-cache 默认会对数据做 JSON 序列化,若 SearchDataFile 包含不可序列化内容(如函数、循环引用),会导致缓存失效。可临时关闭克隆测试:

constructor() {
  this.myCache = new NodeCache({
    stdTTL: 7000,
    checkperiod: 120,
    useClones: false
  });
}

3. 应对 Firebase Functions 冷启动

云函数空闲时会被销毁,缓存数据随之丢失。高频访问数据建议改用 Firebase Memorystore(Redis),或调整函数最小实例数减少冷启动频率。

4. 添加实例一致性日志

在缓存类中添加实例ID日志,确认是否为同一缓存实例:

constructor() {
  this.myCache = new NodeCache({stdTTL: 7000, checkperiod: 120});
  logger.debug(`Cache instance ID: ${this.myCache.id}`);
}
public get(key: string): SearchDataFile | undefined {
  const value = this.myCache.get(key);
  logger.debug(`Get ${key} from cache ${this.myCache.id}: ${value}`);
  return value;
}

内容的提问来源于stack exchange,提问作者Gbenga B Ayannuga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 12:07:47