views:

412

answers:

5

How can I check files that I already processed in a script so I don't process those again? and/or What is wrong with the way I am doing this now?

Hello, I am running tshark with the ring buffer option to dump to files after 5MB or 1 hour. I wrote a python script to read these files in XML and dump into a database, this works fine.

My issue is that this is really process intensive, one of those 5MB can turn into a 200MB file when converted to XML, so I do not want to do any unnecessary processing.

The script is running every 10 minutes and processes ~5 files per run, since is scanning the folder where the files are created for any new entries, I dump a hash of the file into the database and on the next run check the hash and if it isn't in the database I scan the file. The problem is that this does not appear to work every time, it ends up processing files that it has already done. When I check the hash of the file that it keeps trying to process it doesn't show up anywhere in the database, hence why is trying to process it over and over.

I am printing out the filename + hash in the output of the script:

using file /var/ss01/SS01_00086_20100107100828.cap with hash: 982d664b574b84d6a8a5093889454e59
using file /var/ss02/SS02_00053_20100106125828.cap with hash: 8caceb6af7328c4aed2ea349062b74e9
using file /var/ss02/SS02_00075_20100106184519.cap with hash: 1b664b2e900d56ca9750d27ed1ec28fc
using file /var/ss02/SS02_00098_20100107104437.cap with hash: e0d7f5b004016febe707e9823f339fce 
using file /var/ss02/SS02_00095_20100105132356.cap with hash: 41a3938150ec8e2d48ae9498c79a8d0c 
using file /var/ss02/SS02_00097_20100107103332.cap with hash: 4e08b6926c87f5967484add22a76f220
using file /var/ss02/SS02_00090_20100105122531.cap with hash: 470b378ee5a2f4a14ca28330c2009f56
using file /var/ss03/SS03_00089_20100107104530.cap with hash: 468a01753a97a6a5dfa60418064574cc 
using file /var/ss03/SS03_00086_20100105122537.cap with hash: 1fb8641f10f733384de01e94926e0853
using file /var/ss03/SS03_00090_20100107105832.cap with hash: d6209e65348029c3d211d1715301b9f8 
using file /var/ss03/SS03_00088_20100107103248.cap with hash: 56a26b4e84b853e1f2128c831628c65e 
using file /var/ss03/SS03_00072_20100105093543.cap with hash: dca18deb04b7c08e206a3b6f62262465 
using file /var/ss03/SS03_00050_20100106140218.cap with hash: 36761e3f67017c626563601eaf68a133 
using file /var/ss04/SS04_00010_20100105105912.cap with hash: 5188dc70616fa2971d57d4bfe029ec46 
using file /var/ss04/SS04_00071_20100107094806.cap with hash: ab72eaddd9f368e01f9a57471ccead1a 
using file /var/ss04/SS04_00072_20100107100234.cap with hash: 79dea347b04a05753cb4ff3576883494 
using file /var/ss04/SS04_00070_20100107093350.cap with hash: 535920197129176c4d7a9891c71e0243 
using file /var/ss04/SS04_00067_20100107084826.cap with hash: 64a88ecc1253e67d49e3cb68febb2e25 
using file /var/ss04/SS04_00042_20100106144048.cap with hash: bb9bfa773f3bf94fd3af2514395d8d9e 
using file /var/ss04/SS04_00007_20100105101951.cap with hash: d949e673f6138af2d388884f4a6b0f08

The only files it should be doing are one per folder, so only 4 files. This causes unecessary processing and I have to deal with overlapping cron jobs + other services been affected.

What I am hoping to get from this post is a better way to do this or hopefully someone can tell me why is happening, I know that the latter might be hard since it can be a bunch of reasons.

Here is the code (I am not a coder but a sys admin so be kind :P) line 30-32 handle the hash comparisons. Thanks in advance.

A: 

I see several issues.

If you have overlapping cron jobs you need to have a locking mechanism to control access. Only allow one process at a time to eliminate the overlap problem. You might setup a shell script to do that. Create a 'lock' by making a directory (mkdir is atomic), process the data, then delete the lock directory. If the shell script finds the directory already exists when it tries to make it then you know another copy is already running and it can just exit.

If you can't change the cron table(s) then just rename the executable and name your shell script the same as the old executable.

Hashes are not guaranteed to be unique identifiers for files, it's likely they are, but it's not absolutely guaranteed.

Jay
overlapping cron jobs is one of my problems but not my only problem, I am actually using http://unixwiz.net/tools/lockrun.html as a solution for that , but the job takes a lot longer to run than needed due to the unnecessary processing of already used files. If I have to deal with this so be it, but I was trying to find a way to better do this with a hash or something else.
salparadise
+2  A: 

I don't know enough about what is in these files, so this may not work for you, but if you have only one intended consumer, I would recommend using directories and moving the files to reflect their state. Specifically, you could have a dir structure like

/waiting
/progress
/done

and use the relative atomicity of mv to change the "state" of each file. (Whether mv is truly atomic depends on your filesystem, I believe.)

When your processing task wants to work on a file, it moves it from waiting to progress (and makes sure that the move succeeded). That way, no other task can pick it up, since it's no longer waiting. When the file is complete, it gets moved from progress to done, where a cleanup task might delete or archive old files that are no longer needed.

MikeSep
Sounds promising. I will do some testing with this concept. I will update with what I come up with. Thanks.
salparadise
A: 

Why not just move a processed file to a different directory?

You mentioned overlapping cron jobs. Does this mean one conversion process can start before the previous one finished? That means you would perform the move at the beginning of the conversion. If you are worries about an interrupted conversion, use an intermediate directory, and move to a final directory after completion.

gary
OK - we were all responding at the same time...
gary
A: 

If I'm reading the code correctly, you're updating the database (by which I mean the log of files processed) at the very end. So when you have a huge file that's being processed and not yet complete, another cron job will 'legally' start working on it. - both completing succesfully resulting in two entries in the database.

I suggest you move up the logging-to-database, which would act as a lock for subsequent cronjobs and having a 'success' or 'completed' at the very end. The latter part is important as something that's shown as processing but doesnt have a completed state (coupled with the notion of time) can be programtically concluded as an error. (That is to say, a cronjob tried processing it but never completed it and the log show processing for 1 week!)

To summarize

  1. Move up the log-to-database so that it would act as a lock
  2. Add a 'success' or 'completed' state which would give the notion of errored state

PS: Dont take it in the wrong way, but the code is a little hard to understand. I am not sure whether I do at all.

jeffjose
at least is just a "little hard to understand", you would have cried looking at my perl scripts.
salparadise
+2  A: 

A good way to handle/process files that are created at random times is to use incron rather than cron. (Note: since incron uses the Linux kernel's inotify syscalls, this solution only works with Linux.)

Whereas cron runs a job based on dates and times, incron runs a job based on changes in a monitored directory. For example, you can configure incron to run a job every time a new file is created or modified.

On Ubuntu, the package is called incron. I'm not sure about RedHat, but I believe this is the right package: http://rpmfind.net//linux/RPM/dag/redhat/el5/i386/incron-0.5.9-1.el5.rf.i386.html.

Once you install the incron package, read

man 5 incrontab 

for information on how to setup the incrontab config file. Your incron_config file might look something like this:

/var/ss01/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss02/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss03/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss04/ IN_CLOSE_WRITE /path/to/processing/script.py $#

Then to register this config with the incrond daemon, you'd run

incrontab /path/to/incron_config

That's all there is to it. Now whenever a file is created in /var/ss01, /var/ss02, /var/ss03 or /var/ss04, the command

/path/to/processing/script.py $#

is run, with $# replaced by the name of the newly created file.

This will obviate the need to store/compare hashes, and files will only get processed once -- immediately after they are created.

Just make sure your processing script does not write into the top level of the monitored directories. If it does, then incrond will notice the new file created, and launch script.py again, sending you into an infinite loop.

incrond monitors individual directories, and does not recursively monitor subdirectories. So you could direct tshark to write to /var/ss01/tobeprocessed, use incron to monitor /var/ss01/tobeprocessed, and have your script.py write to /var/ss01, for example.

PS. There is also a python interface to inotify, called pyinotify. Unlike incron, pyinotify can recursively monitor subdirectories. However, in your case, I don't think the recursive monitoring feature is useful or necessary.

unutbu
actually this sounds like the most promising method.So if tshark created file "file1" and it takes an hour to finish creating piping info to it as is running, incron is going to detect that is a modified file and it needs to process it?Thanks.
salparadise
Right, that is how incron should work. By the way, you may have to add your username to /etc/incron.allow before running `incrontab`...
unutbu
Sorry, I think I made a mistake in my original post. I edited my post to use IN_CLOSE_WRITE instead of IN_CREATE, since it is the IN_CLOSE_WRITE event is the one that happens after the file is closed.
unutbu
cool. This looks great, I will test and update.
salparadise
so before I get started on this, do you know if it supports NFS? my google-fu is failing.
salparadise
I don't have first-hand experience, but according to http://beagle-project.org/FAQ, inotify (and presumably therefore incron) will work only if the changes to the NFS directory are made locally (on the linux machine). Remote changes will not trigger an inotify event.
unutbu