From 75814431f00a8d6d61229a7a756dcab871f67b28 Mon Sep 17 00:00:00 2001 From: Boyd Meier Date: Tue, 14 Jan 2014 15:41:39 -0600 Subject: [PATCH 1/8] merge in change from @johnarnold to bump up the block size --- lib/filewatch/tail.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index 3e0c275..6c27bfe 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -173,7 +173,7 @@ def _read_file(path, &block) changed = false loop do begin - data = @files[path].sysread(4096) + data = @files[path].sysread(32768) changed = true @buffers[path].extract(data).each do |line| yield(path, line) From e2970630170481a930d39c7632e9d1fec45c3747 Mon Sep 17 00:00:00 2001 From: Bruce Danziger Date: Thu, 20 Feb 2014 20:32:09 -0500 Subject: [PATCH 2/8] enhancing sincedb to properly flush when files have been read from, while preserving the sincedb_write_interval --- filewatch.gemspec | 2 +- lib/filewatch/tail.rb | 51 ++++++++++++++++++++++++++++++++++-------- lib/filewatch/watch.rb | 5 +++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/filewatch.gemspec b/filewatch.gemspec index 7a59a8b..7b17839 100644 --- a/filewatch.gemspec +++ b/filewatch.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |spec| end spec.name = "filewatch" - spec.version = "0.5.0" + spec.version = "0.5.2" spec.summary = "filewatch - file watching for ruby" spec.description = "Watch files and directories in ruby. Also supports tailing and glob file patterns." spec.files = files diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index 6c27bfe..a2197fb 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -37,6 +37,7 @@ def initialize(opts={}) @sincedb = {} @sincedb_last_write = 0 @statcache = {} + @sincedb_write_pending = false @opts = { :sincedb_write_interval => 10, :stat_interval => 1, @@ -96,6 +97,9 @@ def subscribe(&block) @files[path].close @files.delete(path) @statcache.delete(path) + when :noupdate + @logger.debug(":noupdate for #{path}, from @files") + _sincedb_write_if_pending # will check to see if sincedb_write requests are pending else @logger.warn("unknown event type #{event} for #{path}") end @@ -186,20 +190,14 @@ def _read_file(path, &block) end if changed - now = Time.now.to_i - delta = now - @sincedb_last_write - if delta >= @opts[:sincedb_write_interval] - @logger.debug("writing sincedb (delta since last write = #{delta})") - _sincedb_write - @sincedb_last_write = now - end + _sincedb_write end end # def _read_file public def sincedb_write(reason=nil) @logger.debug("caller requested sincedb write (#{reason})") - _sincedb_write + _sincedb_write(true) # since this is an external request, force the write end private @@ -222,7 +220,38 @@ def _sincedb_open end # def _sincedb_open private - def _sincedb_write + def _sincedb_write_if_pending + + # Check to see if sincedb should be written out since there was a file read after the sincedb flush, + # and during the sincedb_write_interval + + if @sincedb_write_pending + _sincedb_write + end + end + + private + def _sincedb_write(sincedb_force_write=false) + + # This routine will only write out sincedb if enough time has passed based on @sincedb_write_interval + # If it hasn't and we were asked to write, then we are pending a write. + + # if we were called with force == true, then we have to write sincedb and bypass a time check + # ie. external caller calling the public sincedb_write method + + if (!sincedb_force_write) + now = Time.now.to_i + delta = now - @sincedb_last_write + + # we will have to flush out the sincedb file after the interval expires. So, we will try again later. + if delta < @opts[:sincedb_write_interval] + @sincedb_write_pending = true + return + end + end + + @logger.debug("writing sincedb (delta since last write = #{delta})") + path = @opts[:sincedb_path] tmp = "#{path}.new" begin @@ -242,6 +271,10 @@ def _sincedb_write rescue => e @logger.warn("_sincedb_write rename/sync failed: #{tmp} -> #{path}: #{e}") end + + @sincedb_last_write = now + @sincedb_write_pending = false + end # def _sincedb_write public diff --git a/lib/filewatch/watch.rb b/lib/filewatch/watch.rb index 8715130..5f78c38 100644 --- a/lib/filewatch/watch.rb +++ b/lib/filewatch/watch.rb @@ -90,6 +90,11 @@ def each(&block) elsif stat.size > @files[path][:size] @logger.debug("#{path}: file grew, old size #{@files[path][:size]}, new size #{stat.size}") yield(:modify, path) + else + # since there is no update, we should pass control back in case the caller needs to do any work + # otherwise, they can ONLY do other work when a file is created or modified + @logger.debug("#{path}: nothing to update") + yield(:noupdate, path) end @files[path][:size] = stat.size From a376432a64a821a569490d0b15eb4d0060b8f60f Mon Sep 17 00:00:00 2001 From: Michio Nikaido Date: Fri, 30 May 2014 10:28:47 -0700 Subject: [PATCH 3/8] Add follow_only_path flag to dumb down filewatching to just paths --- lib/filewatch/tail.rb | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index 6c27bfe..a808a44 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -42,7 +42,8 @@ def initialize(opts={}) :stat_interval => 1, :discover_interval => 5, :exclude => [], - :start_new_files_at => :end + :start_new_files_at => :end, + :follow_only_path => false }.merge(opts) if !@opts.include?(:sincedb_path) @opts[:sincedb_path] = File.join(ENV["HOME"], ".sincedb") if ENV.include?("HOME") @@ -128,13 +129,22 @@ def _open_file(path, event) stat = File::Stat.new(path) - if @iswindows - fileId = Winhelper.GetWindowsUniqueFileIdentifier(path) - inode = [fileId, stat.dev_major, stat.dev_minor] + if @opts[:follow_only_path] + # In cases where files are rsynced to the consuming server, inodes will change when + # updated files overwrite original ones, resulting in inode changes. In order to + # avoid having the sincedb.member check from failing in this scenario, we'll + # construct the inode key using the path which will be 'stable' + inode = [path, stat.dev_major, stat.dev_minor] else - inode = [stat.ino.to_s, stat.dev_major, stat.dev_minor] + if @iswindows + fileId = Winhelper.GetWindowsUniqueFileIdentifier(path) + inode = [fileId, stat.dev_major, stat.dev_minor] + else + inode = [stat.ino.to_s, stat.dev_major, stat.dev_minor] end - + end + + @statcache[path] = inode if @sincedb.member?(inode) From bcbaebd75d75f472043cb4a298986497edf19f97 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Thu, 5 Jun 2014 12:20:30 +0200 Subject: [PATCH 4/8] Change sincedb file inner separtor to work on path with space --- lib/filewatch/tail.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index 4a8175f..6c75a5a 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -222,7 +222,7 @@ def _sincedb_open @logger.debug("_sincedb_open: reading from #{path}") db.each do |line| - ino, dev_major, dev_minor, pos = line.split(" ", 4) + ino, dev_major, dev_minor, pos = line.split("*", 4) inode = [ino, dev_major.to_i, dev_minor.to_i] @logger.debug("_sincedb_open: setting #{inode.inspect} to #{pos.to_i}") @sincedb[inode] = pos.to_i @@ -272,7 +272,7 @@ def _sincedb_write(sincedb_force_write=false) end @sincedb.each do |inode, pos| - db.puts([inode, pos].flatten.join(" ")) + db.puts([inode, pos].flatten.join("*")) end db.close From 1a3b5771517f59cb281537d8f5f8f7fbd1bc5ee3 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Thu, 3 Jul 2014 19:58:26 +0200 Subject: [PATCH 5/8] Fix sincedb write --- lib/filewatch/tail.rb | 30 +++++++++++++++++++++--------- lib/filewatch/watch.rb | 14 +++++++------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index 6c75a5a..befee93 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -35,9 +35,10 @@ def initialize(opts={}) @watch = FileWatch::Watch.new @watch.logger = @logger @sincedb = {} - @sincedb_last_write = 0 - @statcache = {} + @sincedb_last_write = Time.now.to_i @sincedb_write_pending = false + @sincedb_writing = false + @statcache = {} @opts = { :sincedb_write_interval => 10, :stat_interval => 1, @@ -100,7 +101,7 @@ def subscribe(&block) @statcache.delete(path) when :noupdate @logger.debug(":noupdate for #{path}, from @files") - _sincedb_write_if_pending # will check to see if sincedb_write requests are pending + _sincedb_write_if_pending # will check to see if sincedb_write requests are pending else @logger.warn("unknown event type #{event} for #{path}") end @@ -148,7 +149,6 @@ def _open_file(path, event) end end - @statcache[path] = inode if @sincedb.member?(inode) @@ -200,7 +200,7 @@ def _read_file(path, &block) end if changed - _sincedb_write + _sincedb_write end end # def _read_file @@ -227,6 +227,7 @@ def _sincedb_open @logger.debug("_sincedb_open: setting #{inode.inspect} to #{pos.to_i}") @sincedb[inode] = pos.to_i end + db.close end # def _sincedb_open private @@ -236,7 +237,7 @@ def _sincedb_write_if_pending # and during the sincedb_write_interval if @sincedb_write_pending - _sincedb_write + _sincedb_write end end @@ -249,13 +250,21 @@ def _sincedb_write(sincedb_force_write=false) # if we were called with force == true, then we have to write sincedb and bypass a time check # ie. external caller calling the public sincedb_write method + if(@sincedb_writing) + @logger.warn("_sincedb_write already writing") + return + end + + @sincedb_writing = true + if (!sincedb_force_write) now = Time.now.to_i delta = now - @sincedb_last_write - # we will have to flush out the sincedb file after the interval expires. So, we will try again later. + # we will have to flush out the sincedb file after the interval expires. So, we will try again later. if delta < @opts[:sincedb_write_interval] - @sincedb_write_pending = true + @sincedb_write_pending = true + @sincedb_writing = false return end end @@ -268,6 +277,7 @@ def _sincedb_write(sincedb_force_write=false) db = File.open(tmp, "w") rescue => e @logger.warn("_sincedb_write failed: #{tmp}: #{e}") + @sincedb_writing = false return end @@ -284,7 +294,9 @@ def _sincedb_write(sincedb_force_write=false) @sincedb_last_write = now @sincedb_write_pending = false - + @sincedb_writing = false + + System.gc() end # def _sincedb_write public diff --git a/lib/filewatch/watch.rb b/lib/filewatch/watch.rb index 5f78c38..7b2db06 100644 --- a/lib/filewatch/watch.rb +++ b/lib/filewatch/watch.rb @@ -78,7 +78,7 @@ def each(&block) else inode = [stat.ino.to_s, stat.dev_major, stat.dev_minor] end - + if inode != @files[path][:inode] @logger.debug("#{path}: old inode was #{@files[path][:inode].inspect}, new is #{inode.inspect}") yield(:delete, path) @@ -90,11 +90,11 @@ def each(&block) elsif stat.size > @files[path][:size] @logger.debug("#{path}: file grew, old size #{@files[path][:size]}, new size #{stat.size}") yield(:modify, path) - else - # since there is no update, we should pass control back in case the caller needs to do any work + else + # since there is no update, we should pass control back in case the caller needs to do any work # otherwise, they can ONLY do other work when a file is created or modified @logger.debug("#{path}: nothing to update") - yield(:noupdate, path) + yield(:noupdate, path) end @files[path][:size] = stat.size @@ -157,14 +157,14 @@ def _discover_file(path, initial=false) :inode => [stat.ino, stat.dev_major, stat.dev_minor], :create_sent => false, } - - if @iswindows + + if @iswindows fileId = Winhelper.GetWindowsUniqueFileIdentifier(path) @files[file][:inode] = [fileId, stat.dev_major, stat.dev_minor] else @files[file][:inode] = [stat.ino.to_s, stat.dev_major, stat.dev_minor] end - + if initial @files[file][:initial] = true end From 2d9210211bec32ed39f6f4e7eb67f590b16090aa Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Tue, 2 Jun 2015 16:34:46 +0200 Subject: [PATCH 6/8] Fix too much end --- lib/filewatch/watch.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/filewatch/watch.rb b/lib/filewatch/watch.rb index 7f16477..3d80889 100644 --- a/lib/filewatch/watch.rb +++ b/lib/filewatch/watch.rb @@ -183,7 +183,6 @@ def _discover_file(path, initial=false) :create_sent => false, :initial => initial } - end end end # def _discover_file From a9f884a7bb3725621abe649689452131794cc455 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Wed, 3 Jun 2015 12:46:01 +0200 Subject: [PATCH 7/8] Fix no sincedb write at start (sincedb_write_pending init) Fix @sincedb[sincedb_record_uid] null when addin a file Fix missing end keyword Fix typo --- lib/filewatch/tail.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index c39ea9e..d2086c1 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -37,7 +37,7 @@ def initialize(opts={}) @watch.logger = @logger @sincedb = {} @sincedb_last_write = Time.now.to_i - @sincedb_write_pending = false + @sincedb_write_pending = true @sincedb_writing = false @statcache = {} @opts = { @@ -171,6 +171,7 @@ def _open_file(path, event) end else @logger.debug? && @logger.debug("#{path}: staying at position 0, no sincedb") + @sincedb[sincedb_record_uid] = 0 end return true @@ -274,6 +275,7 @@ def _sincedb_write(sincedb_force_write=false) IO.write(path, serialize_sincedb, 0) else File.atomic_write(path) {|file| file.write(serialize_sincedb) } + end rescue => e @logger.warn("_sincedb_write failed: #{tmp}: #{e}") @sincedb_writing = false @@ -289,7 +291,7 @@ def _sincedb_write(sincedb_force_write=false) public def quit - _sincedb_write(true + _sincedb_write(true) @watch.quit end # def quit From 7ad74218b0b19461d859bd6eef34be685dcdf6e2 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Thu, 4 Jun 2015 15:29:53 +0200 Subject: [PATCH 8/8] Fix sincedb writed before ending current work. --- lib/filewatch/tail.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/filewatch/tail.rb b/lib/filewatch/tail.rb index d2086c1..a77ab19 100644 --- a/lib/filewatch/tail.rb +++ b/lib/filewatch/tail.rb @@ -291,8 +291,8 @@ def _sincedb_write(sincedb_force_write=false) public def quit - _sincedb_write(true) @watch.quit + _sincedb_write(true) end # def quit private