Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Built-in event filtering of Ops #7

Open
nathany opened this issue Jun 28, 2014 · 6 comments
Open

Built-in event filtering of Ops #7

nathany opened this issue Jun 28, 2014 · 6 comments
Labels

Comments

@nathany
Copy link
Contributor

nathany commented Jun 28, 2014

The current mechanism for filtering events is to inspect the Op bitmask:

if event.Op&fsnotify.Create == fsnotify.Create {

In howeyc/fsnotify there is a WatchFlags method to filter operations. WatchFlags was removed in 10e1440 (v0.10.0) because:

  • The implementation required a lot of extra bookkeeping while providing little benefit over filtering events as they were received.
  • As @mstum reported, it wasn't working on Windows, which was due to the bookkeeping not being implemented for files within a directory watch: WatchFlags not supported on Windows? howeyc/fsnotify#93 (comment).
  • There were no tests for WatchFlags.

@nightlyone pointed out that it still is desirable to specify filters so the kernel doesn't need to wakeup our thread/process for an event we'll just ignore.

Some research (API doc) reveal differences from one OS to the next:

  • kqueue doesn't have a Create Op at all (NOTE_CREATE), much less a filter for it.
  • Windows has a filter for FILE_NOTIFY_CHANGE_FILE_NAME that covers Create, Remove and Rename. An event does indicate which one of these it is, but some user-space filtering is still necessary.
@nathany nathany added the API label Jun 28, 2014
@nathany
Copy link
Contributor Author

nathany commented Jun 28, 2014

My current suggestion is to set the Op filter globally at the Watcher level instead of for each Watch added. Preferably in such a way that it cannot be changed later. Perhaps something like:

watcher, err := fsnotify.NewWatcher(fsnotify.Create|fsnotify.Write)
  • The filter can be passed to each adapter to have the OS filter out events the best it can.
  • A final user-space filter can catch any outliers without requiring the significant amount of bookkeeping of the previous implementation.
  • For the same reasons that directory watches are easier to manage, a user-space recursive watcher will be simpler to implement, whether included in fsnotify or externally.

@nightlyone
Copy link

I like your proposal of doing the Ops filtering on creation of the watcher.

One usually creates such a watcher with a purpose in mind and the events being delivered are usually suitable for this purpose for the lifetime of the watcher. And after all this is just an optimization. One can always choose a broader scope in order to be sure.

So maybe an additional simple creation function like NewGreedyWatcher might be useful OR an additional complex creation function like NewFilteredWatcher. One of those is meant to be without arguments.

@nathany nathany added this to the Version 2 API milestone Aug 17, 2014
@nathany nathany modified the milestone: v2 API changes Sep 24, 2014
@tsuna
Copy link

tsuna commented Oct 12, 2016

The current code of Add() contains:

    const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
        unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
        unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF

    var flags uint32 = agnosticEvents

So basically the flags are hard-coded to the ones above. Would there be any opposition to adding another API, like:

func (w *Watcher) Add(name string) error {
    return w.AddWithFlags(name, agnosticEvents)
}

func (w *Watcher) AddWithFlags(name string, flags uint32) error {
    // mostly the same code as the current Add() but use flags passed in argument

I need to ignore IN_DELETE_SELF because it's misleading in my case (and causes correctness issues in my code), I only care about IN_DELETE.

@nathany
Copy link
Contributor Author

nathany commented Oct 13, 2016

@tsuna The problem with adding a new API like that is that the flags differ from platform to platform. That's partly why I'd like to split out inotify #173 so it can be used independently in special Linux-only cases.

This issue is tracking a more general solution, which may be a ways off yet.

@tsuna
Copy link

tsuna commented Oct 13, 2016

I hear you, although I personally don't think it's a big problem that the flags differ across the platforms, since this API would just take a uint32 and what values make sense for this API could be platform specific. So this would be a more advanced API for those needing the extra control.

Now when I reviewed the open issues I missed #173, that's obviously a more elegant solution, and at this rate between this bug and #97 and #123, it's very hard for me to get correctness out of fsnotify, so I was considering writing my own little inotify wrapper anyway, so I'm glad to hear that this is being considered.

For now I think I'll just fork the project to quickly fix/hack the bugs that are impacting me.

@nathany
Copy link
Contributor Author

nathany commented Oct 13, 2016

Pull requests are welcome, and once we have more people reviewing and merging pull requests, things should start to move faster.

I think breaking out the low-level platform specific libraries (such as #173) will also help there. It should allow experts in inotify to work on that without worrying about the cross-platform API, and others to tackle cross-platform consistency without dealing with all the low-level details.

tsuna added a commit to aristanetworks/fsnotify that referenced this issue Oct 16, 2016
On Linux, implementing a recursive watch with IN_DELETE_SELF is
virtually impossible.  Since the API doesn't offer any way to pick
which flags to use (see issue fsnotify#7), such an API is needed, and this
is a crude but effective attempt at adding one.
0x1997 pushed a commit to 0x1997/fsnotify that referenced this issue Dec 6, 2016
On Linux, implementing a recursive watch with IN_DELETE_SELF is
virtually impossible.  Since the API doesn't offer any way to pick
which flags to use (see issue fsnotify#7), such an API is needed, and this
is a crude but effective attempt at adding one.
@arp242 arp242 removed the API label Jul 29, 2022
tomponline added a commit to tomponline/lxd that referenced this issue Nov 24, 2022
…notify/fsnotify package

There is an unfortunate behaviour in the fsnotify package in that it watches for all events
on a path, and doesn't give the user the option of only watching for specific events.

This means that when watching, for example on /dev/net, any writes to /dev/net/tun will cause
a WRITE event to be sent to LXD. As you can image, for high traffic interfaces this can cause
many events and causes LXD to use a lot of CPU. This is equally the case for other devices
that can be written to, such as block devices.

The issue is identified in the upstream package (fsnotify/fsnotify#7)
but there doesn't seem to be any consensus on how to move forward so the issue has languished.

LXD however doesn't need cross-platform support that fsnotify package offers and so can use a
lightweight wrapper around the inotify feature (which is what we use with fsnotify anyway).

There used to be an inotify wrapper at golang.org/x/exp/inotify which has subsequently moved
to gopkg.in/fsnotify.v0. Unfortunately that doesn't support go mod and so isn't usable by
LXD. Its also not been updated since 2015.

There is however a fork of golang.org/x/exp/inotify at https://github.com/kubernetes/utils/tree/master/inotify
which has seen some minor maintenance this year. This package also has go mod support.

This commit also makes the following changes:
 - Avoids the need to make a stat syscall to detect if event is for a directory by using the
   InIsdir flag in the event Mask.
 - Avoids calling every registered handler for every new directory and instead performs a path
   prefix match.
 - Improves comments and readability.
 - Cleans the path coming from the event to ensure comparison with registered handlers works.
 - Reduces repitition in event action logic.

Fixes canonical#11151

Signed-off-by: Thomas Parrott <thomas.parrott@canonical.com>
tomponline added a commit to tomponline/lxd that referenced this issue Nov 24, 2022
…notify/fsnotify package

There is an unfortunate behaviour in the fsnotify package in that it watches for all events
on a path, and doesn't give the user the option of only watching for specific events.

This means that when watching, for example on /dev/net, any writes to /dev/net/tun will cause
a WRITE event to be sent to LXD. As you can image, for high traffic interfaces this can cause
many events and causes LXD to use a lot of CPU. This is equally the case for other devices
that can be written to, such as block devices.

The issue is identified in the upstream package (fsnotify/fsnotify#7)
but there doesn't seem to be any consensus on how to move forward so the issue has languished.

LXD however doesn't need cross-platform support that fsnotify package offers and so can use a
lightweight wrapper around the inotify feature (which is what we use with fsnotify anyway).

There used to be an inotify wrapper at golang.org/x/exp/inotify which has subsequently moved
to gopkg.in/fsnotify.v0. Unfortunately that doesn't support go mod and so isn't usable by
LXD. Its also not been updated since 2015.

There is however a fork of golang.org/x/exp/inotify at https://github.com/kubernetes/utils/tree/master/inotify
which has seen some minor maintenance this year. This package also has go mod support.

This commit also makes the following changes:
 - Avoids the need to make a stat syscall to detect if event is for a directory by using the
   InIsdir flag in the event Mask.
 - Avoids calling every registered handler for every new directory and instead performs a path
   prefix match.
 - Improves comments and readability.
 - Cleans the path coming from the event to ensure comparison with registered handlers works.
 - Reduces repetition in event action logic.

Fixes canonical#11151

Signed-off-by: Thomas Parrott <thomas.parrott@canonical.com>
tomponline added a commit to tomponline/lxd that referenced this issue Nov 24, 2022
…notify/fsnotify package

There is an unfortunate behaviour in the fsnotify package in that it watches for all events
on a path, and doesn't give the user the option of only watching for specific events.

This means that when watching, for example on /dev/net, any writes to /dev/net/tun will cause
a WRITE event to be sent to LXD. As you can image, for high traffic interfaces this can cause
many events and causes LXD to use a lot of CPU. This is equally the case for other devices
that can be written to, such as block devices.

The issue is identified in the upstream package (fsnotify/fsnotify#7)
but there doesn't seem to be any consensus on how to move forward so the issue has languished.

LXD however doesn't need cross-platform support that fsnotify package offers and so can use a
lightweight wrapper around the inotify feature (which is what we use with fsnotify anyway).

There used to be an inotify wrapper at golang.org/x/exp/inotify which has subsequently moved
to gopkg.in/fsnotify.v0. Unfortunately that doesn't support go mod and so isn't usable by
LXD. Its also not been updated since 2015.

There is however a fork of golang.org/x/exp/inotify at https://github.com/kubernetes/utils/tree/master/inotify
which has seen some minor maintenance this year. This package also has go mod support.

This commit also makes the following changes:
 - Avoids the need to make a stat syscall to detect if event is for a directory by using the
   InIsdir flag in the event Mask.
 - Avoids calling every registered handler for every new directory and instead performs a path
   prefix match.
 - Improves comments and readability.
 - Cleans the path coming from the event to ensure comparison with registered handlers works.
 - Reduces repetition in event action logic.

Fixes canonical#11151

Signed-off-by: Thomas Parrott <thomas.parrott@canonical.com>
stgraber pushed a commit to canonical/lxd that referenced this issue Jan 10, 2023
…notify/fsnotify package

There is an unfortunate behaviour in the fsnotify package in that it watches for all events
on a path, and doesn't give the user the option of only watching for specific events.

This means that when watching, for example on /dev/net, any writes to /dev/net/tun will cause
a WRITE event to be sent to LXD. As you can image, for high traffic interfaces this can cause
many events and causes LXD to use a lot of CPU. This is equally the case for other devices
that can be written to, such as block devices.

The issue is identified in the upstream package (fsnotify/fsnotify#7)
but there doesn't seem to be any consensus on how to move forward so the issue has languished.

LXD however doesn't need cross-platform support that fsnotify package offers and so can use a
lightweight wrapper around the inotify feature (which is what we use with fsnotify anyway).

There used to be an inotify wrapper at golang.org/x/exp/inotify which has subsequently moved
to gopkg.in/fsnotify.v0. Unfortunately that doesn't support go mod and so isn't usable by
LXD. Its also not been updated since 2015.

There is however a fork of golang.org/x/exp/inotify at https://github.com/kubernetes/utils/tree/master/inotify
which has seen some minor maintenance this year. This package also has go mod support.

This commit also makes the following changes:
 - Avoids the need to make a stat syscall to detect if event is for a directory by using the
   InIsdir flag in the event Mask.
 - Avoids calling every registered handler for every new directory and instead performs a path
   prefix match.
 - Improves comments and readability.
 - Cleans the path coming from the event to ensure comparison with registered handlers works.
 - Reduces repetition in event action logic.

Fixes #11151

Signed-off-by: Thomas Parrott <thomas.parrott@canonical.com>
arp242 added a commit that referenced this issue May 1, 2024
This adds the ability to only listen for some event types. If you're
only interested in Created events and you're getting a lot of Write
events then you're just wasting CPU cycles

This also adds the ability listen on extra unportable event types; since
this is so related I figured I might as well do both. Ideally we want to
1) make it very very obvious you're doing something unportable, and 2)
make it reasonably easy "fallback" for platforms where this isn't
supported.

Unportable events start with "Unportable", which should document their
unportabilitiness. Also add a new Supports(Op) method, which should make
adding fallback logic relatively painless.

For example, to use CloseWrite where supported, but falling back to
Write when it's not:

	var op fsnotify.Op
	if w.Supports(fsnotify.UnportableCloseWrite) {
		op |= fsnotify.UnportableCloseWrite
	} else {
		op |= fsnotify.Create | fsnotify.Write
	}
	w.AddWith("/tmp", fsnotify.WithEvents(op))

And then you can deal with this in the write loop. There's a full
example in cmd/fsnotify/closewrite.go

All of this is unexported for now, until support for other platforms has
been added.

Updates #7
Updates #519
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants