chore: remove watcher polling option (#7480)

* remove watcher polling

* fix lint

* add db migration
This commit is contained in:
Jonathan Jogenfors 2024-02-28 21:20:10 +01:00 committed by GitHub
parent 784d92dbb3
commit e4f32a045d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 18 additions and 107 deletions

View File

@ -88,10 +88,7 @@ Some basic examples:
This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. Deleted assets are, as always, marked as offline and can be removed with the "Remove offline files" button. This feature - currently hidden in the config file - is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. Deleted assets are, as always, marked as offline and can be removed with the "Remove offline files" button.
If your photos are on a network drive you will likely have to enable filesystem polling. The performance hit for polling large libraries is currently unknown, feel free to test this feature and report back. In addition to the boolean feature flag, the configuration file allows customization of the following parameters, please see the [chokidar documentation](https://github.com/paulmillr/chokidar?tab=readme-ov-file#performance) for reference. If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes.
- `usePolling` (default: `false`).
- `interval`. (default: 10000). When using polling, this is how often (in milliseconds) the filesystem is polled.
### Nightly job ### Nightly job

View File

@ -9,8 +9,6 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**enabled** | **bool** | | **enabled** | **bool** | |
**interval** | **int** | |
**usePolling** | **bool** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -14,37 +14,25 @@ class SystemConfigLibraryWatchDto {
/// Returns a new [SystemConfigLibraryWatchDto] instance. /// Returns a new [SystemConfigLibraryWatchDto] instance.
SystemConfigLibraryWatchDto({ SystemConfigLibraryWatchDto({
required this.enabled, required this.enabled,
required this.interval,
required this.usePolling,
}); });
bool enabled; bool enabled;
int interval;
bool usePolling;
@override @override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigLibraryWatchDto && bool operator ==(Object other) => identical(this, other) || other is SystemConfigLibraryWatchDto &&
other.enabled == enabled && other.enabled == enabled;
other.interval == interval &&
other.usePolling == usePolling;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(enabled.hashCode) + (enabled.hashCode);
(interval.hashCode) +
(usePolling.hashCode);
@override @override
String toString() => 'SystemConfigLibraryWatchDto[enabled=$enabled, interval=$interval, usePolling=$usePolling]'; String toString() => 'SystemConfigLibraryWatchDto[enabled=$enabled]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'enabled'] = this.enabled; json[r'enabled'] = this.enabled;
json[r'interval'] = this.interval;
json[r'usePolling'] = this.usePolling;
return json; return json;
} }
@ -57,8 +45,6 @@ class SystemConfigLibraryWatchDto {
return SystemConfigLibraryWatchDto( return SystemConfigLibraryWatchDto(
enabled: mapValueOfType<bool>(json, r'enabled')!, enabled: mapValueOfType<bool>(json, r'enabled')!,
interval: mapValueOfType<int>(json, r'interval')!,
usePolling: mapValueOfType<bool>(json, r'usePolling')!,
); );
} }
return null; return null;
@ -107,8 +93,6 @@ class SystemConfigLibraryWatchDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'enabled', 'enabled',
'interval',
'usePolling',
}; };
} }

View File

@ -21,16 +21,6 @@ void main() {
// TODO // TODO
}); });
// int interval
test('to test the property `interval`', () async {
// TODO
});
// bool usePolling
test('to test the property `usePolling`', () async {
// TODO
});
}); });

View File

@ -9831,18 +9831,10 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean" "type": "boolean"
},
"interval": {
"type": "integer"
},
"usePolling": {
"type": "boolean"
} }
}, },
"required": [ "required": [
"enabled", "enabled"
"interval",
"usePolling"
], ],
"type": "object" "type": "object"
}, },

View File

@ -4401,18 +4401,6 @@ export interface SystemConfigLibraryWatchDto {
* @memberof SystemConfigLibraryWatchDto * @memberof SystemConfigLibraryWatchDto
*/ */
'enabled': boolean; 'enabled': boolean;
/**
*
* @type {number}
* @memberof SystemConfigLibraryWatchDto
*/
'interval': number;
/**
*
* @type {boolean}
* @memberof SystemConfigLibraryWatchDto
*/
'usePolling': boolean;
} }
/** /**
* *

View File

@ -835,8 +835,6 @@ export type SystemConfigLibraryScanDto = {
}; };
export type SystemConfigLibraryWatchDto = { export type SystemConfigLibraryWatchDto = {
enabled: boolean; enabled: boolean;
interval: number;
usePolling: boolean;
}; };
export type SystemConfigLibraryDto = { export type SystemConfigLibraryDto = {
scan: SystemConfigLibraryScanDto; scan: SystemConfigLibraryScanDto;

View File

@ -112,20 +112,13 @@ export class LibraryService extends EventEmitter {
ignore: library.exclusionPatterns, ignore: library.exclusionPatterns,
}); });
const config = await this.configCore.getConfig();
const { usePolling, interval } = config.library.watch;
this.logger.debug(`Settings for watcher: usePolling: ${usePolling}, interval: ${interval}`);
let _resolve: () => void; let _resolve: () => void;
const ready$ = new Promise<void>((resolve) => (_resolve = resolve)); const ready$ = new Promise<void>((resolve) => (_resolve = resolve));
this.watchers[id] = this.storageRepository.watch( this.watchers[id] = this.storageRepository.watch(
library.importPaths, library.importPaths,
{ {
usePolling, usePolling: false,
interval,
binaryInterval: interval,
ignoreInitial: true, ignoreInitial: true,
}, },
{ {

View File

@ -1,12 +1,9 @@
import { validateCronExpression } from '@app/domain'; import { validateCronExpression } from '@app/domain';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { import {
IsBoolean, IsBoolean,
IsInt,
IsNotEmpty, IsNotEmpty,
IsObject, IsObject,
IsPositive,
IsString, IsString,
Validate, Validate,
ValidateIf, ValidateIf,
@ -38,14 +35,6 @@ export class SystemConfigLibraryScanDto {
export class SystemConfigLibraryWatchDto { export class SystemConfigLibraryWatchDto {
@IsBoolean() @IsBoolean()
enabled!: boolean; enabled!: boolean;
@IsBoolean()
usePolling!: boolean;
@IsInt()
@IsPositive()
@ApiProperty({ type: 'integer' })
interval!: number;
} }
export class SystemConfigLibraryDto { export class SystemConfigLibraryDto {

View File

@ -132,8 +132,6 @@ export const defaults = Object.freeze<SystemConfig>({
}, },
watch: { watch: {
enabled: false, enabled: false,
usePolling: false,
interval: 10_000,
}, },
}, },
server: { server: {

View File

@ -136,8 +136,6 @@ const updatedConfig = Object.freeze<SystemConfig>({
}, },
watch: { watch: {
enabled: false, enabled: false,
usePolling: false,
interval: 10_000,
}, },
}, },
}); });

View File

@ -51,8 +51,6 @@ export enum SystemConfigKey {
LIBRARY_SCAN_CRON_EXPRESSION = 'library.scan.cronExpression', LIBRARY_SCAN_CRON_EXPRESSION = 'library.scan.cronExpression',
LIBRARY_WATCH_ENABLED = 'library.watch.enabled', LIBRARY_WATCH_ENABLED = 'library.watch.enabled',
LIBRARY_WATCH_USE_POLLING = 'library.watch.usePolling',
LIBRARY_WATCH_INTERVAL = 'library.watch.interval',
LOGGING_ENABLED = 'logging.enabled', LOGGING_ENABLED = 'logging.enabled',
LOGGING_LEVEL = 'logging.level', LOGGING_LEVEL = 'logging.level',
@ -268,8 +266,6 @@ export interface SystemConfig {
}; };
watch: { watch: {
enabled: boolean; enabled: boolean;
usePolling: boolean;
interval: number;
}; };
}; };
server: { server: {

View File

@ -0,0 +1,12 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`);
await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`);
}
public async down(): Promise<void> {
// noop
}
}

View File

@ -42,28 +42,6 @@
subtitle="Watch external libraries for file changes" subtitle="Watch external libraries for file changes"
bind:checked={config.library.watch.enabled} bind:checked={config.library.watch.enabled}
/> />
<SettingSwitch
title="Use filesystem polling (EXPERIMENTAL)"
disabled={disabled || !config.library.watch.enabled}
subtitle="Use polling instead of native filesystem watching. This is required for network shares but can be very resource intensive. Use with care!"
bind:checked={config.library.watch.usePolling}
/>
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
required={config.library.watch.usePolling}
disabled={disabled || !config.library.watch.usePolling || !config.library.watch.enabled}
label="Polling interval"
bind:value={config.library.watch.interval}
isEdited={config.library.watch.interval !== savedConfig.library.watch.interval}
>
<svelte:fragment slot="desc">
<p class="text-sm dark:text-immich-dark-fg">
Interval of filesystem polling, in milliseconds. Lower values will result in higher CPU usage.
</p>
</svelte:fragment>
</SettingInputField>
</div> </div>
<div class="ml-4"> <div class="ml-4">