PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : File-Schnittstelle (großes File zerlegen oder Alternative? "pipe"?)



tschloss
11-11-2006, 09:27
Hi,
ich sitze gerade an einem Prpogrämmchen, welches via vcard_import Kontaktdaten einliest und dabei eine Datenbank aktualisiert.

Das Modul erwartet ein File als Parameter und parst das gesammte File in eine verschachtelte Array Struktur.
Dummerweise enthält mein File ca. 5.000 Einträge. Das mit dem Array klappt zwar noch, aber sobald ich die Einträge mit mehreren Datenbank-Operationen noch verarbeite, läuft er (PHP als Apache Mdoul) in einen Timeout (anderes Thema: trotz "max_execution_time" 360 in php.ini und php5.ini sagt phpinfo() immer 60 sec. XAMPP unter Windows).

Die Frage:
Ich würde das jetzt gerne in Paketen zu 100 oder 500 ausführen.
Alternative 1: ich lese das große File und schreibe immer n Datensätze in ein Tempfile, verarbeite dies und so weiter.
Gibt es eine zweite Altenative, bei der ich kein explizites Tempfile anlegen muß, also so eine Art Pipe in die vcard_import-Funktion? Oder wie macht man das am geschicktesten?

Danke.
Greetz
Thomas

nEox
11-11-2006, 10:07
Hallo Thomas,

setze in der php.ini "max_execution_time" auf "0" und es sollte ohne Probleme durchlaufen. Dauert halt dementsprechend lange.

Wichtig: Bearbeite die Datei "xampp/apache/bin/php.ini"!
Die php.ini im Ordner "php" im Xampp Ordner betrifft nur die CLI-Variante.

Grüße,
Adrian

tschloss
11-11-2006, 12:35
Hallo Thomas,

setze in der php.ini "max_execution_time" auf "0" und es sollte ohne Probleme durchlaufen. Dauert halt dementsprechend lange.

Wichtig: Bearbeite die Datei "xampp/apache/bin/php.ini"!
Die php.ini im Ordner "php" im Xampp Ordner betrifft nur die CLI-Variante.

Grüße,
Adrian

Ok, danke.
Da ich mich soweiso mit dieser Riesenarray-Geschichte nicht wohl fühlte, habe ich mein Progrämmchen jetzt in eine Schleife gepackt, die 200er -weise auf Tempfiles arbeitet. Spätestens wenn das Proggie mal auf einem Webserver landet, bin ich über den reduzierten Hauptspeicherbedarf wieder froh.

Thx
Thomas

BlueJay
13-11-2006, 11:11
setze in der php.ini "max_execution_time" auf "0" und es sollte ohne Probleme durchlaufen.

Wenn du viel mit php rumtestest, willst du das nicht wirklich!
Arbeite lieber mit set_time_limit(dingsbumsda)

so long,
BlueJay

nEox
13-11-2006, 12:54
Wenn du viel mit php rumtestest, willst du das nicht wirklich!
Arbeite lieber mit set_time_limit(dingsbumsda)
Das stimmt. Dann aber auf keinen Fall eine ungetestete Version auf den "Live"-Server laden... sonst kann es passieren das der ganze Apache abraucht (wenn PHP als Modul geladen wurde). Weiterhin kann man die Funktion set_time_limit() nicht mit aktiviertem "safe_mode" aufrufen.

Als "besten Kompromiss" könntest du in deiner Apache-Vhost-Konfiguration, bei XAMPP unter "conf\extra\httpd-vhosts.conf", nur bei deinem aktuellen Projekt die "max_execution_time" per ...

php_admin_value max_execution_time 0
... setzen.

Grüße,
nEox

sticky bit
13-11-2006, 19:15
Kenne die Struktur deiner Datei jetzt nicht, aber wäre es denn nicht möglich einfach immer nur solange aus der Datei in einen Puffer zu lesen bis man einen Datensatz hat, diesen dann auf die Datanbank speichert, dann den Puffer wieder leer und weiter macht? Wenn man dann noch mit Transaktionen arbeitet ist es auch egal wenn in mitten des Vorgang ein Fehler auftritt, dann kann man diese ja rückgängig machen und hat immer noch ne "saubere" Datenbank, sollte das ein Problem darstellen...

tschloss
15-11-2006, 20:52
Kenne die Struktur deiner Datei jetzt nicht, aber wäre es denn nicht möglich einfach immer nur solange aus der Datei in einen Puffer zu lesen bis man einen Datensatz hat, diesen dann auf die Datanbank speichert, dann den Puffer wieder leer und weiter macht? Wenn man dann noch mit Transaktionen arbeitet ist es auch egal wenn in mitten des Vorgang ein Fehler auftritt, dann kann man diese ja rückgängig machen und hat immer noch ne "saubere" Datenbank, sollte das ein Problem darstellen...

Dieses Modul will halt ein File (im Format vcf) als Input. Es gefiel mir einfach nicht, das großeFile häppchenweise in ein kleines umzukopieren. Hätte es lieber über den Speicher gemacht. Aber inzwischen mache ich es über ein tempfile (im Moment 200er-weise).

Thx
Thomas

sticky bit
15-11-2006, 23:42
Dieses Modul will halt ein File (im Format vcf) als Input. Es gefiel mir einfach nicht, das großeFile häppchenweise in ein kleines umzukopieren. Hätte es lieber über den Speicher gemacht. Aber inzwischen mache ich es über ein tempfile (im Moment 200er-weise).

Thx
Thomas
Genau das meint ich ja, du sollst es in einen Puffer im Speicher laden. Aber ich kenn die Struktur eines vcf nicht (und bin zugegebenermassen da jetzt zu faul mich rein zu lesen und zu denken), ob diese evtl. das ganze extem erschwert oder so. Aber wenn wir mal ein einfaches Format annehmen, dass sequentiell Datensätze hält, dann hätt ich halt in etwa sowas vorgeschlagen (Pseudo-Code):


$record;
$filehandle;
$databasehandle;

if (!($filehandle = openFile("myfile"))) {
printError("Could not open file!\n");
exit OPEN_FILE;
}

if (!($databasehandle = connectToDatabase("mydatabase"))) {
printError("Could not open database connection!\n");
exit CONN_2_DB;
}

if (!beginDatabaseTransaction($databasehandle)) {
printError("Could not begin database transaction!\n");
exit BEGIN_DB_TRANSACT;
}

while (($record = getRecord($filehandle))) {
if (!saveRecordToDatabase($record)) {
printError("Could not save record to database!\n");
rollBackTransaction($databasehandle);
exit SAVE_2_DB;
}
}

if (!commitDatabaseTransaction($databasehandle)) {
printError("Could not commit database transaction);
exit COMMIT_DB_TRANSACT;
}

closeConnectionToDatabase($databasehandle);
closeFile($filehandle);

print("File successfully imported!");
exit SUCCESS;

In getRecord() ist halt dann das Lesen aus der Datei implementiert und zwar immer ein ganzer Datensatz. Also in einfaches Beispiel wäre immer eine Zeile als Datensatz anzusehen, dann wäre der Umbruch halt das Datensatztrennzeichen bis zu dem man immer alle Zeichen in einen Puffer liest, so etwa (wieder nur Pseudo-Code):


function getRecord ($fielhandle)
{
#define RECORD_SEP '\n'
$buffer;
$c;

$buffer = "";
while (($c = getc($filehandle))) {
if ($c == RECORD_SEP) {
return $buffer;
}
else {
$buffer .= $c;
}
}

return EOF;
}

Aber natürlich kann die Funktion jegliche andere Logik implementieren einen Satz aus der Datei zu lesen, sei es nach einer Blockgrösse, ggf. auch variabel aus einem Block-Header ermittelt oder nach Schlüsselworten, denkbar wäre hier z. B. XML, lese alles vom nächsten <record> bis zum dazugehörigen </record>, etc., pp....
Oder man lässt das mit der Funktion und schreibts direkt in Code rein (muss man halt den Puffer wieder leeren wenn man den Datensatz in die Datenbank weggespeichert hat), oder, oder, oder...

Sowas ist doch eigentlich das was du wolltest oder, also ohne irgendwelche temporären Dateien?

Zwar läuft das dann natürlich erst mal wegen dem erhöten Aufwand an E/A-Operationen etwas langsamer als wenn du das ganze in einem Rutsch in den Speicher laden würdest, aber bei grossen Dateien belegst du nicht den ganzen physikalischen Speicher wodurch das Betriebssystem anfängt auzulagern und du dann natürlich E/A auf die Platte doppelt und dreifach hast der den Prozess ausbremst (und alle anderen Prozesse ringsherum u. U. auch noch mit runtergezogen werden)...

tschloss
21-11-2006, 11:14
Vielen Dank für Deine Mühe, sticky bit.

Mein Problem war allerdings, dass ich keinen Einfluß auf die Funktion "getRecord" habe. Das VCF-Parsing-Modul ist in sich geschlossen und erwartet als Parameter einen Filenamen! Als Output erhalte ich eine gefüllte, tief strukturierte PHP-Variable.

Letztere arbeite ich dann in einer eigenen Schleife ab und führe DB-Operationen aus.

Wie beschrieben, erzeuge ich inzwischen 200-er-weise ein Tempfile und werfe dieses jeweils dem VCF-Parser vor.

Grüße
Thomas