1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 11:15:54 +02:00

refactor: config caching (#10168)

This commit is contained in:
Jason Rasmussen 2024-06-12 07:07:35 -04:00 committed by GitHub
parent 5dda5d93f5
commit e84657192c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 54 additions and 54 deletions

View File

@ -254,7 +254,7 @@ export class StorageCore {
this.logger.warn(`Unable to complete move. File size mismatch: ${newPathSize} !== ${oldPathSize}`);
return false;
}
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: true });
if (assetInfo && config.storageTemplate.hashVerificationEnabled) {
const { checksum } = assetInfo;
const newChecksum = await this.cryptoRepository.hashFile(newPath);

View File

@ -42,8 +42,8 @@ export class SystemConfigCore {
instance = null;
}
async getConfig(force = false): Promise<SystemConfig> {
if (force || !this.config) {
async getConfig({ withCache }: { withCache: boolean }): Promise<SystemConfig> {
if (!withCache || !this.config) {
const lastUpdated = this.lastUpdated;
await this.asyncLock.acquire(DatabaseLock[DatabaseLock.GetSystemConfig], async () => {
if (lastUpdated === this.lastUpdated) {
@ -74,13 +74,13 @@ export class SystemConfigCore {
await this.repository.set(SystemMetadataKey.SYSTEM_CONFIG, partialConfig);
const config = await this.getConfig(true);
const config = await this.getConfig({ withCache: false });
this.config$.next(config);
return config;
}
async refreshConfig() {
const newConfig = await this.getConfig(true);
const newConfig = await this.getConfig({ withCache: false });
this.config$.next(newConfig);
}

View File

@ -245,7 +245,7 @@ export class AssetService {
}
async handleAssetDeletionCheck(): Promise<JobStatus> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const trashedDays = config.trash.enabled ? config.trash.days : 0;
const trashedBefore = DateTime.now()
.minus(Duration.fromObject({ days: trashedDays }))

View File

@ -77,7 +77,7 @@ export class AuthService {
}
async login(dto: LoginCredentialDto, details: LoginDetails) {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
if (!config.passwordLogin.enabled) {
throw new UnauthorizedException('Password login has been disabled');
}
@ -174,7 +174,7 @@ export class AuthService {
}
async authorize(dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
if (!config.oauth.enabled) {
throw new BadRequestException('OAuth is not enabled');
}
@ -190,7 +190,7 @@ export class AuthService {
}
async callback(dto: OAuthCallbackDto, loginDetails: LoginDetails) {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const profile = await this.getOAuthProfile(config, dto.url);
this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`);
let user = await this.userRepository.getByOAuthId(profile.sub);
@ -242,7 +242,7 @@ export class AuthService {
}
async link(auth: AuthDto, dto: OAuthCallbackDto): Promise<UserAdminResponseDto> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const { sub: oauthId } = await this.getOAuthProfile(config, dto.url);
const duplicate = await this.userRepository.getByOAuthId(oauthId);
if (duplicate && duplicate.id !== auth.user.id) {
@ -264,7 +264,7 @@ export class AuthService {
return LOGIN_URL;
}
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
if (!config.oauth.enabled) {
return LOGIN_URL;
}

View File

@ -42,25 +42,25 @@ export class CliService {
}
async disablePasswordLogin(): Promise<void> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
config.passwordLogin.enabled = false;
await this.configCore.updateConfig(config);
}
async enablePasswordLogin(): Promise<void> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
config.passwordLogin.enabled = true;
await this.configCore.updateConfig(config);
}
async disableOAuthLogin(): Promise<void> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
config.oauth.enabled = false;
await this.configCore.updateConfig(config);
}
async enableOAuthLogin(): Promise<void> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
config.oauth.enabled = true;
await this.configCore.updateConfig(config);
}

View File

@ -43,7 +43,7 @@ export class DuplicateService {
}
async handleQueueSearchDuplicates({ force }: IBaseJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
if (!isDuplicateDetectionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -64,7 +64,7 @@ export class DuplicateService {
}
async handleSearchDuplicates({ id }: IEntityJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
if (!isDuplicateDetectionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}

View File

@ -150,7 +150,7 @@ export class JobService {
}
async init(jobHandlers: Record<JobName, JobHandler>) {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
for (const queueName of Object.values(QueueName)) {
let concurrency = 1;

View File

@ -67,7 +67,7 @@ export class LibraryService {
}
async init() {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const { watch, scan } = config.library;

View File

@ -47,7 +47,7 @@ export class MapService {
}
async getMapStyle(theme: 'light' | 'dark') {
const { map } = await this.configCore.getConfig();
const { map } = await this.configCore.getConfig({ withCache: false });
const styleUrl = theme === 'dark' ? map.darkStyle : map.lightStyle;
if (styleUrl) {

View File

@ -149,7 +149,7 @@ export class MediaService {
}
async handleAssetMigration({ id }: IEntityJob): Promise<JobStatus> {
const { image } = await this.configCore.getConfig();
const { image } = await this.configCore.getConfig({ withCache: true });
const [asset] = await this.assetRepository.getByIds([id]);
if (!asset) {
return JobStatus.FAILED;
@ -164,7 +164,7 @@ export class MediaService {
async handleGeneratePreview({ id }: IEntityJob): Promise<JobStatus> {
const [{ image }, [asset]] = await Promise.all([
this.configCore.getConfig(),
this.configCore.getConfig({ withCache: true }),
this.assetRepository.getByIds([id], { exifInfo: true }),
]);
if (!asset) {
@ -185,7 +185,7 @@ export class MediaService {
}
private async generateThumbnail(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
const { image, ffmpeg } = await this.configCore.getConfig();
const { image, ffmpeg } = await this.configCore.getConfig({ withCache: true });
const size = type === AssetPathType.PREVIEW ? image.previewSize : image.thumbnailSize;
const path = StorageCore.getImagePath(asset, type, format);
this.storageCore.ensureFolders(path);
@ -237,7 +237,7 @@ export class MediaService {
async handleGenerateThumbnail({ id }: IEntityJob): Promise<JobStatus> {
const [{ image }, [asset]] = await Promise.all([
this.configCore.getConfig(),
this.configCore.getConfig({ withCache: true }),
this.assetRepository.getByIds([id], { exifInfo: true }),
]);
if (!asset) {
@ -318,7 +318,7 @@ export class MediaService {
return JobStatus.FAILED;
}
const { ffmpeg } = await this.configCore.getConfig();
const { ffmpeg } = await this.configCore.getConfig({ withCache: true });
const target = this.getTranscodeTarget(ffmpeg, mainVideoStream, mainAudioStream);
if (target === TranscodeTarget.NONE) {
if (asset.encodedVideoPath) {

View File

@ -137,7 +137,7 @@ export class MetadataService {
this.subscription = this.configCore.config$.subscribe(() => handlePromiseError(this.init(), this.logger));
}
const { reverseGeocoding } = await this.configCore.getConfig();
const { reverseGeocoding } = await this.configCore.getConfig({ withCache: false });
const { enabled } = reverseGeocoding;
if (!enabled) {
@ -333,7 +333,7 @@ export class MetadataService {
private async applyReverseGeocoding(asset: AssetEntity, exifData: ExifEntityWithoutGeocodeAndTypeOrm) {
const { latitude, longitude } = exifData;
const { reverseGeocoding } = await this.configCore.getConfig();
const { reverseGeocoding } = await this.configCore.getConfig({ withCache: true });
if (!reverseGeocoding.enabled || !longitude || !latitude) {
return;
}

View File

@ -68,7 +68,7 @@ export class NotificationService {
throw new HttpException('Failed to verify SMTP configuration', HttpStatus.BAD_REQUEST, { cause: error });
}
const { server } = await this.configCore.getConfig();
const { server } = await this.configCore.getConfig({ withCache: false });
const { html, text } = this.notificationRepository.renderEmail({
template: EmailTemplate.TEST_EMAIL,
data: {
@ -94,7 +94,7 @@ export class NotificationService {
return JobStatus.SKIPPED;
}
const { server } = await this.configCore.getConfig();
const { server } = await this.configCore.getConfig({ withCache: true });
const { html, text } = this.notificationRepository.renderEmail({
template: EmailTemplate.WELCOME,
data: {
@ -137,7 +137,7 @@ export class NotificationService {
const attachment = await this.getAlbumThumbnailAttachment(album);
const { server } = await this.configCore.getConfig();
const { server } = await this.configCore.getConfig({ withCache: false });
const { html, text } = this.notificationRepository.renderEmail({
template: EmailTemplate.ALBUM_INVITE,
data: {
@ -179,7 +179,7 @@ export class NotificationService {
const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) => user.id !== senderId);
const attachment = await this.getAlbumThumbnailAttachment(album);
const { server } = await this.configCore.getConfig();
const { server } = await this.configCore.getConfig({ withCache: false });
for (const recipient of recipients) {
const user = await this.userRepository.get(recipient.id, { withDeleted: false });
@ -220,7 +220,7 @@ export class NotificationService {
}
async handleSendEmail(data: IEmailJob): Promise<JobStatus> {
const { notifications } = await this.configCore.getConfig();
const { notifications } = await this.configCore.getConfig({ withCache: false });
if (!notifications.smtp.enabled) {
return JobStatus.SKIPPED;
}

View File

@ -88,7 +88,7 @@ export class PersonService {
}
async getAll(auth: AuthDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
const people = await this.repository.getAllForUser(auth.user.id, {
minimumFaceCount: machineLearning.facialRecognition.minFaces,
withHidden: dto.withHidden || false,
@ -282,7 +282,7 @@ export class PersonService {
}
async handleQueueDetectFaces({ force }: IBaseJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
if (!isFacialRecognitionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -313,7 +313,7 @@ export class PersonService {
}
async handleDetectFaces({ id }: IEntityJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
if (!isFacialRecognitionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -371,7 +371,7 @@ export class PersonService {
}
async handleQueueRecognizeFaces({ force }: IBaseJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
if (!isFacialRecognitionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -402,7 +402,7 @@ export class PersonService {
}
async handleRecognizeFaces({ id, deferred }: IDeferrableJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
if (!isFacialRecognitionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -486,7 +486,7 @@ export class PersonService {
}
async handleGeneratePersonThumbnail(data: IEntityJob): Promise<JobStatus> {
const { machineLearning, image } = await this.configCore.getConfig();
const { machineLearning, image } = await this.configCore.getConfig({ withCache: true });
if (!isFacialRecognitionEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}

View File

@ -95,7 +95,7 @@ export class SearchService {
}
async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise<SearchResponseDto> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
if (!isSmartSearchEnabled(machineLearning)) {
throw new BadRequestException('Smart search is not enabled');
}

View File

@ -65,7 +65,7 @@ export class ServerInfoService {
async getFeatures(): Promise<ServerFeaturesDto> {
const { reverseGeocoding, map, machineLearning, trash, oauth, passwordLogin, notifications } =
await this.configCore.getConfig();
await this.configCore.getConfig({ withCache: false });
return {
smartSearch: isSmartSearchEnabled(machineLearning),
@ -85,12 +85,12 @@ export class ServerInfoService {
}
async getTheme() {
const { theme } = await this.configCore.getConfig();
const { theme } = await this.configCore.getConfig({ withCache: false });
return theme;
}
async getConfig(): Promise<ServerConfigDto> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const isInitialized = await this.userRepository.hasAdmin();
const onboarding = await this.systemMetadataRepository.get(SystemMetadataKey.ADMIN_ONBOARDING);

View File

@ -40,7 +40,7 @@ export class SmartInfoService {
await this.jobRepository.waitForQueueCompletion(QueueName.SMART_SEARCH);
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
await this.databaseRepository.withLock(DatabaseLock.CLIPDimSize, () =>
this.repository.init(machineLearning.clip.modelName),
@ -50,7 +50,7 @@ export class SmartInfoService {
}
async handleQueueEncodeClip({ force }: IBaseJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: false });
if (!isSmartSearchEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}
@ -75,7 +75,7 @@ export class SmartInfoService {
}
async handleEncodeClip({ id }: IEntityJob): Promise<JobStatus> {
const { machineLearning } = await this.configCore.getConfig();
const { machineLearning } = await this.configCore.getConfig({ withCache: true });
if (!isSmartSearchEnabled(machineLearning)) {
return JobStatus.SKIPPED;
}

View File

@ -110,7 +110,7 @@ export class StorageTemplateService {
}
async handleMigrationSingle({ id }: IEntityJob): Promise<JobStatus> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: true });
const storageTemplateEnabled = config.storageTemplate.enabled;
if (!storageTemplateEnabled) {
return JobStatus.SKIPPED;
@ -140,7 +140,7 @@ export class StorageTemplateService {
async handleMigration(): Promise<JobStatus> {
this.logger.log('Starting storage template migration');
const { storageTemplate } = await this.configCore.getConfig();
const { storageTemplate } = await this.configCore.getConfig({ withCache: true });
const { enabled } = storageTemplate;
if (!enabled) {
this.logger.log('Storage template migration disabled, skipping');

View File

@ -42,7 +42,7 @@ export class SystemConfigService {
}
async init() {
const config = await this.core.getConfig();
const config = await this.core.getConfig({ withCache: false });
this.config$.next(config);
}
@ -51,7 +51,7 @@ export class SystemConfigService {
}
async getConfig(): Promise<SystemConfigDto> {
const config = await this.core.getConfig();
const config = await this.core.getConfig({ withCache: false });
return mapConfig(config);
}
@ -71,7 +71,7 @@ export class SystemConfigService {
throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use');
}
const oldConfig = await this.core.getConfig();
const oldConfig = await this.core.getConfig({ withCache: false });
try {
await this.eventRepository.serverSendAsync(ServerAsyncEvent.CONFIG_VALIDATE, {
@ -110,7 +110,7 @@ export class SystemConfigService {
}
async getCustomCss(): Promise<string> {
const { theme } = await this.core.getConfig();
const { theme } = await this.core.getConfig({ withCache: false });
return theme.customCss;
}

View File

@ -128,7 +128,7 @@ export class UserService {
async handleUserDeleteCheck(): Promise<JobStatus> {
const users = await this.userRepository.getDeletedUsers();
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
await this.jobRepository.queueAll(
users.flatMap((user) =>
this.isReadyForDeletion(user, config.user.deleteDelay)
@ -140,7 +140,7 @@ export class UserService {
}
async handleUserDelete({ id, force }: IEntityJob): Promise<JobStatus> {
const config = await this.configCore.getConfig();
const config = await this.configCore.getConfig({ withCache: false });
const user = await this.userRepository.get(id, { withDeleted: true });
if (!user) {
return JobStatus.FAILED;

View File

@ -56,7 +56,7 @@ export class VersionService {
return JobStatus.SKIPPED;
}
const { newVersionCheck } = await this.configCore.getConfig();
const { newVersionCheck } = await this.configCore.getConfig({ withCache: true });
if (!newVersionCheck.enabled) {
return JobStatus.SKIPPED;
}