Skip to content

Commit

Permalink
$mol_file watcher native fix
Browse files Browse the repository at this point in the history
  • Loading branch information
zerkalica committed Nov 15, 2024
1 parent 1742d9e commit 8186ecc
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 56 deletions.
2 changes: 1 addition & 1 deletion build/build.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace $ {

@ $mol_mem_key
static root( [ root, paths ] : [root: string, paths: readonly string[] ] ) {
this.$.$mol_file.watch_root = root
this.$.$mol_file.base = root

return this.make({
root : ()=> this.$.$mol_file.absolute( root ) ,
Expand Down
44 changes: 19 additions & 25 deletions file/base/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ namespace $ {
@ $mol_action
exists_cut() { return this.exists() }

protected root() {
const path = this.path()
const base = (this.constructor as typeof $mol_file_base).base

// Если путь выше или равен base - считаем это корнем
return base.startsWith(path)
}

@ $mol_mem
protected stat(next? : $mol_file_stat | null, virt?: 'virt') {

Expand All @@ -34,29 +42,20 @@ namespace $ {

// Отслеживать проверку наличия родительской папки не стоит до корня диска
// Лучше ограничить mam-ом
const root = (this.constructor as typeof $mol_file_base).watch_root
if ( path !== root && path !== parent.path() ) {
if ( ! this.root() ) {
/*
Если папка удалилась, надо ресетнуть все объекты в ней на любой глубине.
rm -rf с последующим git pull: parent папка может удалиться, потом создасться, а текущая папка только удалиться.
Поэтому parent.exists() не запустит перевычисления
Если parent папка удалилась, надо ресетнуть все объекты в ней на любой глубине.
Например, rm -rf с последующим git pull: parent папка может удалиться, потом создасться,
а текущая папка успеет только удалиться до момента выполнения stat.
Поэтому parent.exists() не запустит перевычисления, нужна именно parent.version()
parent.version() меняется не только при удалении, будет ложное срабатывание
Если addDir будет сбрасывать parent.version(), то будет лишний раз перевычислен parent, хоть и он сам не поменялся
Однако, parent.version() меняется не только при удалении, будет ложное срабатывание
С этим придется мириться, красивого решения пока нет.
*/

parent.version()

// родительской папки может не быть, например, из foo/bar/baz на диске есть только foo
// baz.stat запустит bar.watcher и node.fs.watch упадет с ошибкой, т.к. папки bar нет
if (parent.exists()) parent.watcher()
} else {
// watch_root - это корень mam или диска, считаем, что он всегда существует
// если тут делать exists проверку, как строчками выше,
// то будет выполнен stat во всех верхних папках до корня диска,
// что делать не стоит из-за прав доступа, к примеру
parent.watcher()
}
parent.watcher()

if( virt ) return next ?? null

Expand Down Expand Up @@ -241,9 +240,6 @@ namespace $ {
// Если файл записали, потом отключили вотчер, кто-то из вне его поменял, потом включили вотчер, снова записали тот же буфер,
// то буфер не запишется на диск, т.к. кэш не консистентен с диском.

// Также это не поможет, т.к. всякие генерации view.tree используют не идемпотентные id-ки
// При первом старте, даже если есть уже сбилженый view.tree.d.ts, он будет перезаписан.
// watcher триггернется и снова запишет с новыми id и зациклится
if (! changed && this.exists()) return prev

this.parent().exists( true )
Expand Down Expand Up @@ -287,7 +283,7 @@ namespace $ {
return null
}

static watch_root = ''
// static watch_root = ''

// static watcher_warned = false
watcher() {
Expand All @@ -314,12 +310,10 @@ namespace $ {
if( next ) {
this.parent().exists( true )
this.ensure()
this.reset()
return next
} else {
this.drop()
}

this.drop()
// удалили директорию, все дочерние потеряли актуальность
this.reset()

return next
Expand Down
70 changes: 40 additions & 30 deletions file/file.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,48 +50,58 @@ namespace $ {
return this.absolute<This>( $node.path.resolve( this.base, path ).replace( /\\/g , '/' ) )
}

@ $mol_mem_key
static watcher(path: string) {
const watcher = $node.fs.watch( path, (type, name) => {
@ $mol_mem
override watcher(reset?: null) {
const path = this.path()
const root = this.root()
// Если папки/файла нет, watch упадет с ошибкой
// exists обратится к parent.version и parent.watcher
// Поэтому у root-папки и выше не надо вызывать exists, иначе поднимется выше base до корня диска
// exists вызывать надо, что б пересоздавать вотчер при появлении папки или файла
if (! root && ! this.exists() ) return super.watcher()

let watcher

try {
// Между exists и watch файл может удалиться, в любом случае надо обрабатывать ENOENT
watcher = $node.fs.watch( path )
} catch (error: any) {
if ( ! (error instanceof Error) ) error = new Error('Unknown watch error', {cause: error})
error.message += '\n' + path

if ( root || error.code !== 'ENOENT' ) {
this.$.$mol_fail_log(error)
}

// Если файла нет - вотчер не создается, создастся потом, когда exists поменяется на true.
// Если создание упало с другой ошибкой - не ломаем работу mol_file, деградируем до не реактивной fs.

return super.watcher()
}

watcher.on('change', (type: 'change' | 'rename', name) => {
if (! name) return
this.changed_add(type, $node.path.join( path, name ))
const path = $node.path.join( this.path(), name.toString() )
;(this.constructor as typeof $mol_file_base).changed_add(type, path)
})

watcher.on('error', e => this.$.$mol_fail_log(e) )

let destructed = false

watcher.on('close', () => {
// Если в процессе работы вотчер сам закрылся, надо его переоткрыть
if (! destructed) setTimeout(() => $mol_wire_async(this).watcher(null), 500)
})

return {
destructor() {
destructed = true
watcher.close()
}
}

// const watcher = $node.chokidar.watch( path , {
// persistent : true ,
// ignored: path => /([\/\\]\.|___$)/.test( path ),
// depth : 0 ,
// ignoreInitial : true ,
// awaitWriteFinish: {
// stabilityThreshold: 100,
// },
// } )

// watcher
// .on( 'all' , (type: 'add' | 'change' | 'unlink' | 'addDir' | 'unlinkDir', path) => {
// const normalized = type === 'unlink' || type === 'unlinkDir' ? 'rename' : 'change'
// this.changed_add(normalized, path)
// } )
// .on( 'error' , e => this.$.$mol_fail_log(e) )

// return {
// destructor() {
// watcher.close()
// }
// }

}

override watcher() { return this.$.$mol_file_node.watcher(this.path()) }

@ $mol_action
protected override info( path: string ) {
try {
Expand Down

0 comments on commit 8186ecc

Please sign in to comment.