schnelle Variante um bestimmte Dateien zu finden

Errorsmith

Kompiliertier
Hi

Ich habe ein etwas kniffeliges Problem:
Gegeben sind eine FreeBSD Box mit sh & bash installiert. (Ich habe die Möglichkeit bei Bedarf weitere Programme zu installieren). Weiter ein Verzeichnis welches von einem remote-Verzeichnis eingehängt ist. Der entfernte Server ist über eine stabile, aber nicht sehr schnelle Leitung eingebunden. Im Verzeichnis liegen bis zu 2000 (temporäre) Dateien die auf Verarbeitung warten.

Ich schreibe nun ein Skript, welches die älteste Datei ermittelt die dort liegt. Das Ergebnis geht an ein Monitoring-System.
Das Problem ist, das das Monitoring eine maximale Zeit von 30 Sekunden vorgibt, alles darüber ergibt ein Timeout. Alle Varianten die ich versucht (und ergoogelt) habe, verwenden eine Variante von "find" oder "ls" und pipen das Ergebnis davon in mehr oder weniger elegante Verschachtelungen anderer Befehle. Und ergeben Laufzeiten von ca 30 Sekunden (bei ca 1200 Dateien im Verzeichnis). Das ist leider zuviel.

Gibt es eine Möglichkeit dies schneller zu bewältigen? Ein anderes Tool oder sonst irgendwas um das zu beschleunigen?

Bin für jeden Tip dankbar!

Grüße,
errirsmith
 
Inwiefern kannst du (z.B. per ssh) den Server mit den 2000 lokalen Dateien selber nach den ältesten Objekten suchen lassen?
 
Leider garnicht, sonst hätte ich das Problem nicht...
Der Server ist an einem anderen Standort und hängt im Firmen-VPN. Das einzige was ich tun kann ist mir die Freigabe zu mounten und dort zu arbeiten.
 
Hmmm, wenn LS einfach so lange braucht das Verzeichniss zu öffnen, braucht ls vermutlich einfach so lange. Ich glaube nicht das es ein schnelleres tool ist.

Sollte es evtl. an der Kombination liegen (ls ausgabe gepipt in einen anderen Befehl) könntest du noch versuchen erst das ls ergebnis in eine lokale datei zu schreiben und die dann zu pipen für das gewünschte ergebniss - zumindest könntest du auf dem weg auch schauen wo der flaschenhals liegt - lesen von ls / find aus dem verzeichniss (Unabänderbar) oder ausgabe der dateien in die weitere kommandokette.

Vermutlich wird dir aber nur ein längere Zeitlimit oder eine schnellere Anbindung helfen.
 
Hi

Eine schnellere Anbindung werde ich nicht bekommen.
Das Timeout ist konfigurierbar zw 3 und 30 Sekunden, hier stellt 30 Sekunden das Maximum dar. Ich kann mir den Quellcode auf dem System anschauen und eventuell das Teil patchen. Das macht es halt schwer wartbar und ich würde gern darauf verzichten da ich dann auch Pakete / die Ports nicht mehr nutzen kann.

Ich suchte nach einer Möglichkeit das zu regeln ohne das ls erst jede Datei einzeln "anschauen" muß. Ich nahm an es gäbe vielleicht einen Befehl der mir die gewünschten Informationen direkt ausgibt. Ich werde mir morgen mal die Quellen vom Tool anschauen.
Danke erstmal, falls jemand eine Idee hat: Ich bin noch offen.

Grüße,
errorsmith
 
Dann gib dem Monitoring einen veralteten Wert. Der BSD Host lässt das Script immer so lange laufen wie es nunmal braucht und schreibt das Ergebnis lokal in eine Datei.
Der Agent bzw. das Monitoring bekommt dann eine Ausgabe die auf dieser lokalen Datei des BSD Host basiert. Ist dann zwar nicht immer aktuell im Rahmen von 30 Sekunden. Aber vermutlich aktuell genug um auf ein Problem reagieren zu können.
 
Das ist vermutlich das sinnvollste: Ein cronjob (z.b. alle 10 Minuten) und ein zweites script welches den Fileinhalt liest.
Es gibt ein "send" utility mit dem man Daten direkt an den Server senden kann, evtl kann ich das vom cronjob direkt aufrufen, so das die Daten regelmäßig nach dem Cronjob übertragen werden. Das Monitoring kommt mit unregelmäßigen Zeitabständen klar (ist kein rrd Kram).

Danke für die Anrgungen,
errorsmith
 
Wie wäre der proaktive Ansatz: Das Überwachungsprogramm liest beim Start das Verzeichnis einmal ein und merkt sich das Alter aller Dateien. Dann legt ein sich eine kqueue() auf das Verzeichnis und jedes Mal, wenn sich etwas an ihm ändert, kontrolliert es was genau passiert ist. Anhand dessen aktualisiert es seine interne Dateiliste. Anhand der Liste kann er bei jedem Poll durch das Monitoringsystem einfach die älteste Datei ausrechnen.
 
Nachdem ich gegoogelt habe was kqueue() ist:

Zunächst mal bin ich garnicht erst solche Ideen gekommen, da es definitiv den Rahmen eines einfachen Shellscripts sprengen würde. Zweitens müsste ich dann einen daemon programmieren der $irgendwie auf Anfragen lauscht sowie ein zweites Programm (oder Script) das auf Anfrage des Monitorings das erste Programm "befragt". Beides habe ich (in C? C++?) noch nie gemacht und müsste also erstmal C lernen um das ordentlich zu machen... Die Idee ist allerdings nicht schlecht und klingt performant. Ob das dann mit vielen Verzeichnissen (worst case: 20-30 allerdings mit viel weniger Dateien) skaliert müsste ich dann auch noch testen.

Grundsätzlich: Hätte ich wesentlich(!) mehr Zeit als ich tatsächlich habe, würde ich mich vielleicht daran machen. Da ich diese allerdings nicht habe und die Daten nur im Zeitrahmen von 10-15 Minuten genau erfasst werden müssen, werde ich mich zunächst mit dem "send" utility befassen. Ein Blick in die manpage sagt mir, das ich bis 250 Werte in einem Rutsch an den Server durchreichen kann, d.h. ich kann im Skript nur einmal je Verzeichnis die Dateien einlesen die gewünschten Werte alle auf einmal erfassen. (Anzahl, größte, kleinste, neueste, älteste). Die kqueue() Idee hebe ich mir für später auf, zw. den Jahren ist es in der Regel ruhig und da hätte ich Zeit für sowas.

Falls das irgendwie noch relevant ist: Das Monitoring ist zabbix und soll ein Mischmasch aus nagios, prtg, nettools und einem Haufen "cmd" Files auf diversen Windows-Büchsen ablösen.

Die besten Ergebnisse erreiche ich übrigens bisher damit:
Code:
now=$(date +%s)
read name timestamp < <(find "$2" -maxdepth $1 -type f -exec stat -f "%N %m" {} \; | sort -r -k2,2n)
echo $name $(( $now - $timestamp ))

Das braucht bei 1200 Dateien zw 55 und 65 Sekunden.

Grüße,
errorsmith
 
Ich weiß nicht, welche Zusatzinformationen du noch alle brauchst - evtl. reicht ja dieses dreckige Konstrukt:

Code:
find . -type d -exec ls -l -D %Y-%m-%d-%H:%M:%S {} \; | grep -v total |  sort -k 6 | tail -1

Finde alle Unterverzeichnisse
Führe ls -l auf die Unterverzeichnisse aus (-D ist eine FreeBSD-Option - Keine Ahnung, ob die bei 8.x schon da war) und gebe eine Art Zeitstempel mit aus
Lösche die "Zusammenfassungen" des ls-Kommandos
Sortiere nach Spalte 6 (hier kannst du mit "-r" das Ergebnis umdrehen
Gib das letzte aus

Nachteil: Du suchst nicht nach "hidden" files und es ist wirklich nicht schön.

Gruß
Markus
 
Kann es dann nicht sein, dass bei vielen Verzeichnissen die maximale Anzahl an Argumenten erericht wird? (Gut in dem Fall müsste der find-Befehl auch viele Execs starten...)
 
Kann es dann nicht sein, dass bei vielen Verzeichnissen die maximale Anzahl an Argumenten erericht wird? (Gut in dem Fall müsste der find-Befehl auch viele Execs starten...)
xargs -n 1000 .. dann gibt es pro 1000 einen fork und nicht 1000, wie bei find -exec. Was max arglist fuer ls(1) ist, weiss ich grad nicht auswendig fuer FreeBSD
 
Wie Kamikaze schon schreibt, ein "find ... -exec ... +" erspart xargs. "+" setzt die maximal mögliche Anzahl an Dateinamen ein.
 
Zurück
Oben