Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
462d705
feat(flags): add CachedFeaturesRepository with Redis caching
devin-ai-integration[bot] Dec 22, 2025
ca6f843
fix(flags): add version stamps to teamsAutoOptIn cache key
devin-ai-integration[bot] Dec 22, 2025
5eab77e
refactor(flags): simplify to single cache-buster approach
devin-ai-integration[bot] Dec 22, 2025
2ff3e3a
refactor(flags): switch to per-entity caching with batch composition
devin-ai-integration[bot] Dec 22, 2025
56c7fb2
refactor(flags): remove batch composition threshold
devin-ai-integration[bot] Dec 22, 2025
17c88cd
revert: remove DI wiring changes from Features.ts
devin-ai-integration[bot] Dec 22, 2025
59c30ae
fix(flags): address caching issues in CachedFeaturesRepository
devin-ai-integration[bot] Dec 22, 2025
a5cbe35
refactor(flags): add typed cache descriptors for type-safe caching
devin-ai-integration[bot] Dec 22, 2025
cf513a6
refactor(flags): remove FeaturesCacheKeys class and simplify cache en…
devin-ai-integration[bot] Dec 22, 2025
3263b6b
refactor(flags): rename cache helper methods for clarity
devin-ai-integration[bot] Dec 22, 2025
fcb052a
refactor(flags): make FeaturesCacheEntries functions return bound cac…
devin-ai-integration[bot] Dec 22, 2025
60d4bec
refactor(flags): remove explicit return type annotations from Feature…
devin-ai-integration[bot] Dec 22, 2025
7842e2c
clean up
eunjae-lee Dec 23, 2025
8068f42
put cache on getAllFeatures
eunjae-lee Dec 23, 2025
0284207
test(flags): add unit tests for CachedFeaturesRepository
devin-ai-integration[bot] Dec 23, 2025
821ba68
refactor(flags): move CachedFeaturesRepository tests to __tests__ folder
devin-ai-integration[bot] Dec 23, 2025
db39e6d
refactor(flags): extract mocks and consolidate test files in __tests__
devin-ai-integration[bot] Dec 23, 2025
d73fd5f
refactor(flags): move integration test to __tests__ folder
devin-ai-integration[bot] Dec 23, 2025
c4ad7a5
refactor(flags): simplify caching with composite keys (userId/teamId …
devin-ai-integration[bot] Jan 5, 2026
c90a978
refactor(flags): centralize team cache resolution
eunjae-lee Jan 6, 2026
6904b45
fix type error
eunjae-lee Jan 6, 2026
0019709
feat(flags): add value validation in setValue method
devin-ai-integration[bot] Jan 6, 2026
e61306d
Merge branch 'main' into devin/cached-features-repository-1766399596
devin-ai-integration[bot] Jan 8, 2026
9d1cf1c
Merge branch 'main' into devin/cached-features-repository-1766399596
eunjae-lee Jan 13, 2026
ed75937
refactor(flags): add DI support for CachedFeaturesRepository using mo…
devin-ai-integration[bot] Jan 13, 2026
7d5d81f
refactor(flags): wire FeatureOptInService to use CachedFeaturesReposi…
devin-ai-integration[bot] Jan 13, 2026
6748c32
fix(flags): revert FeatureOptInService to use direct FeaturesRepository
devin-ai-integration[bot] Jan 13, 2026
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
16 changes: 16 additions & 0 deletions packages/features/di/containers/CachedFeaturesRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { IFeaturesRepository } from "@calcom/features/flags/features.repository.interface";

import { createContainer } from "../di";
import {
type CachedFeaturesRepository,
moduleLoader as cachedFeaturesRepositoryModuleLoader,
} from "../modules/CachedFeaturesRepository";

const cachedFeaturesRepositoryContainer = createContainer();

export function getCachedFeaturesRepository(): IFeaturesRepository {
cachedFeaturesRepositoryModuleLoader.loadModule(cachedFeaturesRepositoryContainer);
return cachedFeaturesRepositoryContainer.get<CachedFeaturesRepository>(
cachedFeaturesRepositoryModuleLoader.token
);
}
28 changes: 28 additions & 0 deletions packages/features/di/modules/CachedFeaturesRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { FLAGS_DI_TOKENS } from "@calcom/features/flags/di/tokens";
import { CachedFeaturesRepository } from "@calcom/features/flags/cached-features.repository";
import { moduleLoader as redisModuleLoader } from "@calcom/features/redis/di/redisModule";

import { bindModuleToClassOnToken, createModule, type ModuleLoader } from "../di";
import { moduleLoader as featuresRepositoryModuleLoader } from "./FeaturesRepository";

const thisModule = createModule();
const token = FLAGS_DI_TOKENS.CACHED_FEATURES_REPOSITORY;
const moduleToken = FLAGS_DI_TOKENS.CACHED_FEATURES_REPOSITORY_MODULE;

const loadModule = bindModuleToClassOnToken({
module: thisModule,
moduleToken,
token,
classs: CachedFeaturesRepository,
depsMap: {
featuresRepository: featuresRepositoryModuleLoader,
redisService: redisModuleLoader,
},
});

export const moduleLoader: ModuleLoader = {
token,
loadModule,
};

export type { CachedFeaturesRepository };
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ afterEach(async () => {
cleanupFunctions.length = 0;
});

// Access private clearCache method through type assertion
const clearFeaturesCache = (repo: FeaturesRepository) => {
(repo as unknown as { clearCache: () => void }).clearCache();
};

interface TestEntities {
user: { id: number };
org: { id: number };
Expand Down Expand Up @@ -93,8 +88,6 @@ async function setup(): Promise<TestEntities> {
type: "EXPERIMENT",
},
});
// Clear cache after creating feature so getAllFeatures() returns fresh data
clearFeaturesCache(featuresRepository);
return featureSlug;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FeatureId, FeatureState } from "@calcom/features/flags/config";
import type { FeaturesRepository } from "@calcom/features/flags/features.repository";
import type { IFeaturesRepository } from "@calcom/features/flags/features.repository.interface";

import { OPT_IN_FEATURES } from "../config";
import { applyAutoOptIn } from "../lib/applyAutoOptIn";
Expand All @@ -11,7 +11,7 @@ import type { IFeatureOptInService, ResolvedFeatureState } from "./IFeatureOptIn
* Computes effective states based on global, org, team, and user settings.
*/
export class FeatureOptInService implements IFeatureOptInService {
constructor(private featuresRepository: FeaturesRepository) {}
constructor(private featuresRepository: IFeaturesRepository) {}

/**
* Core method: Resolve feature states for a user across all their teams.
Expand Down
Loading
Loading