Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Contao 3.0.6 - Dateisystem #5556

Closed
SGehle opened this issue Mar 27, 2013 · 29 comments
Closed

Contao 3.0.6 - Dateisystem #5556

SGehle opened this issue Mar 27, 2013 · 29 comments
Labels
Milestone

Comments

@SGehle
Copy link

SGehle commented Mar 27, 2013

Beim Synchronisieren des Dateisystems in 3.0.6 tritt folgender Fehler auf:

Fatal error: Uncaught exception Exception with message Query error: Duplicate entry 'PATH' for key 'path' (UPDATE tl_files SET id='744', pid='367', tstamp='1364381093', type='folder', path='PATH1', extension='', hash='d41d8cd98f00b204e9800998ecf8427e', found='', name='_Magnum Parkboxen', meta=NULL WHERE id='744') thrown in system/modules/core/library/Contao/Database/Statement.php on line 317

Die entsprechenden Pfade habe ich durch PATH und PATH1 ersetzt.

Erstaunlich ist, dass der PATH maximal 64 Zeichen lang ist, sobald der reale Pfad länger ist, wird er abgeschnitten.
Ein ähnlicher Fehler wurde bereits im Forum beschrieben, ich habe die Datenbank-Felder überprüft, diese haben allerdings eine maximal-Länge von 255 Zeichen.

Weiterhin habe ich festgestellt,dass PATH1 immer einen Ordner angibt, der keine Dateien enthält. Sobald alle leeren Ordner aus dem Datei-System entfernt sind, tritt keine Fehlermeldung mehr auf.

@leofeyer
Copy link
Member

Wie lässt sich das in der Onlinedemo reproduzieren?

@SGehle
Copy link
Author

SGehle commented Mar 27, 2013

Dazu bräuchte ich FTP-Zugang zur Demo, da die entsprechenden Ordner per FTP hochgeladen wurden.

@leofeyer
Copy link
Member

Ok, anders gefragt: Wie kann ich das in der Music Academy reproduzieren?

@ghost
Copy link

ghost commented Mar 27, 2013

Es wird etwas länglich... Habe mehrere Ordnertiefen und dann eine Datei erzeugt:

  • files/very_long_director_name1/very_long_director_name2/very_long_director_name3/very_long_director_name4/very_long_director_name5-renamed/very_long_director_name6/eine_datei_mit_einem_dateinamen_der_mehr_las_64_zeichen_lang_ist_und_immer_noch_weiter_geht_und_noch_weiter_und_immer_noch_weiter_und_jetzt_bald_nach_eingabe_weiterer_zeichen_schon_160_zeichen_ueberschreitet

(für Tippfehler und mangelde Kreativität bitte ich um Nachsicht).

Sync: in tl_files.path varchar(255) ist der Pfad abgeschnitten, in tl_files.name varchar(64) ebenso der Name. In der BE Dateiverwaltung wird der Name richtig angezeigt (wenngelich das Layout hopps geht) und auch die Links scheinen mir richtig (im HTML source).. Nach Kopieren der Datei unter neuen Namen "zweite_datei...." (also Änderung exstra vor derm truncate point) gibt es folgede Fehlermeldung:

27-Mar-2013 20:24:29] PHP Fatal error: Uncaught exception 'Exception' with message 'Query error: Duplicate entry 'files/very_long_director_name1/very_long_director_name2/very_lon' for key 'path' (INSERT INTO tl_files (pid, tstamp, name, type, path, extension, hash, found) VALUES ('27', 1364415869, 'eine_datei_mit_einem_dateinamen_der_mehr_las_64_zeichen_lang_ist_und_immer_noch_weiter_geht_und_noch_weiter_und_immer_noch_weiter_und_jetzt_bald_nach_eingabe_weiterer_zeichen_schon_160_zeichen_ueberschreitet', 'file', 'files/very_long_director_name1/very_long_director_name2/very_long_director_name3/very_long_director_name4/very_long_director_name5-renamed/very_long_director_name6/eine_datei_mit_einem_dateinamen_der_mehr_las_64_zeichen_lang_ist_und_immer_noch_weiter_geht_und_noch_weiter_und_immer_noch_weiter_und_jetzt_bald_nach_eingabe_weiterer_zeichen_schon_160_zeichen_ueberschreitet', '', 'd41d8cd98f00b204e9800998ecf8427e', 1))' thrown in /srv/www/cto3-link-real/system/modules/core/library/Contao/Database/Statement.php on line 317
#0 /srv/www/cto3-link-real/system/modules/core/library/Contao/Database/Statement.php(261): Contao\Database\Statement->query()
#1 /srv/www/cto3-link-real/system/modules/core/library/Contao/Model.php(252): Contao\Database\Statement->execute()
#2 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2495): Contao\Model->save()
#3 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '27', Array)
#4 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '26', Array)
#5 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '25', Array)
#6 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '24', Array)
#7 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '23', Array)
#8 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2474): Contao\DC_Folder->execSync('files/very_long...', '22', Array)
#9 /srv/www/cto3-link-real/system/modules/core/drivers/DC_Folder.php(2269): Contao\DC_Folder->execSync('files', 0, Array)
#10 /srv/www/cto3-link-real/system/modules/core/classes/Backend.php(384): Contao\DC_Folder->sync()
#11 /srv/www/cto3-link-real/contao/main.php(121): Contao\Backend->getBackendModule('files')
#12 /srv/www/cto3-link-real/contao/main.php(319): Main->run()
#13 {main}

In tl_files ist die zweite Datei nicht drin, in der Dateiverwaltung wird sie brav angezeigt.

Der Fehler liegt m.E in den viel zu kurzen Spaltenlängen von path und name. Im Forum gibt es auch einen Beitrag dazu. In unseren Oracle-Anwendungen (tw. mit mehreren 100.000 files in der DB) sind wir mit 4096 und 1024 hingekommen. Das (an sich ja gute) dbafs hat noch genug Herausforderungen, als dass man sich mit unnötig kurzen Felder noch weitere hereinholen muss (konstruktiv gemeint!).

@ghost
Copy link

ghost commented Mar 27, 2013

MySQL 5 limitiert leider die key length auf 1000, und bei utf8 offenbar sicherheitshalber /4 (utf8 Zeichen können 4 bytes lang sein) - der Versuch path über 255 zu vergrößern wird abgewiesen. Wenn ich das unique constraint auf path lösche und path, name auf 4095, 255 vergrößere, geht der sync problemlos und tl_files enthält die Dateien mit richtigen Angaben. Um die Uniqueness zu sichern, reicht m.E. ein unique key auf (pid, name) - funktioniert bei mir (mySQL 5.1.41).

@ghost
Copy link

ghost commented Mar 27, 2013

Die Spalte path in tl_files ist eigentlich redundant (würde bei jedem Grundkurs SQL rausfliegen), der path ist eigentlich über hierarchische (rekursive) Query über pid bestimmbar (und zwingend so bestimmt). Da mySQL im Ggs. zu Oracle (CONNECT BY) keine hierarchischen Queries kann (und die Simulation ziemlich unperformant ist), könnte man path ggf. in einer Hilfstabelle in der Anwendung oder per Trigger erzeugen. Das ist aber vermutlich ein Thema für eine Grundsatzdiskussion...

@leofeyer
Copy link
Member

MySQL 5 limitiert leider die key length auf 1000, und bei utf8 offenbar sicherheitshalber /4

Ok, das erklärt warum der Key nur 64 Zeichen lang ist.

Ich kann das Problem auch reproduzieren, weiß aber ad hoc keine gute Lösung dafür. Die Spalte "path" ganz wegzulassen, würde eine zusätzliche DB-Abfrage pro Resource bedeuten (vgl. PageModel::loadDetails()), was nicht im Sinne der Performance ist. Außerdem wären Methoden wie FilesModel::findByBasepath() dann wesentlich komplexer und langsamer.

Den UNIQUE-Index möchte ich aber eigentlich auch nicht weglassen, denn der trägt nicht unerheblich zur Datenkonsistenz bei. Eventuell würde es helfen, das Feld auf TEXT zu stellen und einen längeren UNIQUE-Key zu definieren?

@contao/workgroup-core

@leofeyer
Copy link
Member

Eine andere mögliche Lösung wäre eine zusätzliche Spalte mit dem MD5-Wert der "path"-Spalte und dem UNIQUE-Key auf dieser statt der "path"-Spalte.

@ghost
Copy link

ghost commented Mar 28, 2013

Leo, das Problem mit der path-Bestimmung kenne ich auch von Oracle Anwendungen, wo das zwar mit SQL CONNECT BY bequem und auch schnell geht, aber doch intern viele zusätzliche Zugriffe erzeugt. Deswegen von daher mein Vorschlag mit der Hilfstabelle:

Tabelle tl_file_paths enthält nur Einträge für Folder pid und path. PK (gleich FK) auf pid d.h. ID des Folders in tl_files. Tabelle wird automatisch erzeugt und gepflegt, ideal per Trigger auf tl_files sonst in passender class.
Der Zugriff geht dann über einen LEFT JOIN (wenn der für path null bringt, könnte man adhoc einmalig den path nachgenerieren). Das sollte die Performance ok halten.

Das UNIQUE constraint muss nur auf filename (basename) + folder id gesetzt werden - die files müssen ja nur innerhalb eines Folders unique sein (wie im richtigen Dateisystem). So habe ich es auch erfolgreich getestet.

[arroganz]Als altem high-end Datenbankler (Oracle) kräuseln sich mir manchmal die Fußnägel, wenn ich die DB Designs der meisten CMSe sehe[/arroganz]. Das ist verständlich aus dem meist non-DB Hintergrund der Entwickler. Deswegen halte ich mich noch zurück und lese mich erst weiter ein, einschließlich der neuen dbafs classes (***** dafür!). Ich hoffe, dass ich Zeit finde, meinem Vorschlag mal auszuprobieren.

@ghost
Copy link

ghost commented Mar 28, 2013

Hm, nach meiner mySQL Doku ist tinytext mit max. 255 auch nicht länger als varchar(255) in tl_files.path . Meine zum o.g. Test modifizierte tl_files sieht so aus:

CREATE TABLE IF NOT EXISTS `tl_files` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `pid` int(10) unsigned NOT NULL DEFAULT '0',
  `tstamp` int(10) unsigned NOT NULL DEFAULT '0',
  `type` varchar(16) NOT NULL DEFAULT '',
  `path` varchar(4095) NOT NULL DEFAULT '',
  `extension` varchar(16) NOT NULL DEFAULT '',
  `hash` varchar(32) NOT NULL DEFAULT '',
  `found` char(1) NOT NULL DEFAULT '1',
  `name` varchar(255) NOT NULL DEFAULT '',
  `meta` blob,
  PRIMARY KEY (`id`),
  UNIQUE KEY `pidname` (`pid`,`name`),
  KEY `pid` (`pid`),
  KEY `extension` (`extension`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=31 ;

wobei ich path text NULL besser finde (und das funktioniert auch). TEXT ist bis 64k groß.

@leofeyer
Copy link
Member

Tabelle tl_file_paths enthält nur Einträge für Folder pid und path.

Das Problem daran ist, dass die Änderung nicht rückwärtskompatibel ist.

// Aktuell
$file = FilesModel::findByPk(2);
dump($file->path);

// Nach der Änderung
$file = FilesModel::findByPk(2);
dump($file->getRelated('pid')->path);

Der Code könnte also frühestens in Contao 4 einfließen.

Das UNIQUE constraint muss nur auf filename (basename) + folder id gesetzt werden

Da hat Du absolut Recht. Und das ist auch glaube ich die momentan besten Lösung :)

@leofeyer
Copy link
Member

Und das ist auch glaube ich die momentan besten Lösung :)

Nein, leider doch nicht. Denn dann haben wir wieder das Problem, dass MySQL nur maximal 64 Zeichen in den Index schreibt. Auch wenn das bei reinen Dateinamen weniger wahrscheinlich ist als bei Pfaden, bleibt es ein Problem :(

@ghost
Copy link

ghost commented Mar 28, 2013

Das Problem daran ist, dass die Änderung nicht rückwärtskompatibel ist.

Das müssen wir mal auf der Konferenz beschnacken, das wird hier zu lang. Der Zwecke der Models ist doch, den Datenspeicher zu kapseln, also müssen/sollten sie nicht unbedingt Tabellen 1:1 abbilden (Model clients dürfen gar nicht von den unterliegende Strukturen wissen). D.h. es spricht gar nichts dagegen (aber viel dafür), dass FilesModel ein Attribut path liefert wie vorher, während das getRelated in Wirklichkeit fälschlich Implementationsdetails offenlegt. Das hakelt aber wohl derzeit mit Details des sehr generischen DCA Ansatzes, oder?

@ghost
Copy link

ghost commented Mar 28, 2013

Nein, leider doch nicht. Denn dann haben wir wieder das Problem, dass MySQL nur maximal 64 Zeichen in den Index schreibt. Auch wenn das bei reinen Dateinamen weniger wahrscheinlich ist als bei Pfaden, bleibt es ein Problem :(

Das Limit 64 finde ich nicht. laut Doku ist bei Engine MyISAM das Limit 1000 bytes, schon seit 4.1 Zeiten. Das wird zwar auch als uralter Bug moniert (http://bugs.mysql.com/bug.php?id=4541), erlaubt aber allemal 250+ utf8 Zeichen. Ich habe meinem Testcase eine weiter Datei hinzugefügt, die sich von der ersten nur durch weitere angehängte Zeichen unterscheidet (Dateiname insgesamt 211 Zeichen lang). File sync geht problemlos, ebenso Export und anschließender Import (als neu) der Tabelle.

2013-03-28 19_01_55-Dateiverwaltung-very-long-names

Der Index (pid, name) sollte also funktionieren.

@leofeyer
Copy link
Member

Ich habe es gerade getestet und dieselbe Exception wie eingangs erhalten. Ist ja auch klar, wenn der Index-Eintrag nach 64 Zeichen abgeschnitten wird :)

@ghost
Copy link

ghost commented Mar 28, 2013

Verstehe ich nicht. Wer schneidet da ab, wenn du doch die column verlängert hast? Der einzige Grund wäre eine column prefix length a la 'create index xxx on tl_files(path(64))' (http://dev.mysql.com/doc/refman/5.1/en/create-index.html). Sicherheitshalber zitiere ich mich von oben noch mal, weil ich bei deinem Commit fd7245f keine Verlängerung von path gesehen habe:

Hm, nach meiner mySQL Doku ist tinytext mit max. 255 auch nicht länger als varchar(255) in tl_files.path

Wie auch immer, schöne Ostertage!

@leofeyer
Copy link
Member

Verstehe ich nicht. Wer schneidet da ab, wenn du doch die column verlängert hast?

Nicht die Spalte wird abgeschnitten, sondern der Index-Eintrag (siehe oben). Schau doch mal die Fehlermeldungen an, die Du gepostet hast, z.B.

Duplicate entry 'files/very_long_director_name1/very_long_director_name2/very_lon'

Du wirst sehen, dass der Eintrag immer exakt 64 Zeichen lang ist, unabhängig davon, wie lange das Feld ist. Ich habe es mit varchar(64), varchar(255), tinytext und text versucht, aber das Ergebnis war immer dasselbe.

@ghost
Copy link

ghost commented Mar 29, 2013

Dann reden wir noch aneinander vorbei. Ich bekomme keinen Fehler bei meiner modifizierten tl_files (path, name, unique) mit:

CREATE TABLE IF NOT EXISTS `tl_files` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `pid` int(10) unsigned NOT NULL DEFAULT '0',
  `tstamp` int(10) unsigned NOT NULL DEFAULT '0',
  `type` varchar(16) NOT NULL DEFAULT '',
  `path` text NOT NULL,
  `extension` varchar(16) NOT NULL DEFAULT '',
  `hash` varchar(32) NOT NULL DEFAULT '',
  `found` char(1) NOT NULL DEFAULT '1',
  `name` varchar(255) NOT NULL DEFAULT '',
  `meta` blob,
  PRIMARY KEY (`id`),
  UNIQUE KEY `pidname` (`pid`,`name`),
  KEY `pid` (`pid`),
  KEY `extension` (`extension`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=43 ;

Ich habe direkt die Tabelle geändert (phpmyadmin), DCA nicht. Alle noch so langen filenames sind drin, die Cardinality des unique Index ist dieselbe wie des primary Index. Drop und recreate des unique Index funktionieren fehlerfrei. Export und Re-Import der Tabelle geht ebenfalls. Meine mySQL Version ist 5.1.41-3ubuntu12.6. Collation ist überall utf8_general_ci.

Kann es sein, dass du einem mysql bug aufsitzt (dass es sich irgendwo gemerkt hat, dass der key nur 64 Zeichen hat)? Hast du mal den Index gedroppt und neu erstellt?

@leofeyer
Copy link
Member

Hab ich. Das Problem ist, dass der Fehler nicht so einfach zu reproduzieren ist. Am einfachsten geht es wie folgt:

  • Du legst Dir ein paar Unterordner an, so dass der Pfad ca. 60 Zeichen lang ist.
  • In dem Unterordner legst Du zwei gleichnamige Dateien an, die sich nur durch ein Suffix unterscheiden.
  • -> konferenz/konferenz-2013/fotos/allgemein/tag-zwei/sessions/Slideshow-Keynote-1.pdf
  • -> konferenz/konferenz-2013/fotos/allgemein/tag-zwei/sessions/Slideshow-Keynote-2.pdf
  • Dann benennst Du den Ordner sessions um (z.B. in session) und synchronisierst

Auf diese Weise stellst Du sicher, dass zwei neue Einträge in die Datenbank geschrieben werden, deren erste 77 Zeichen übereinstimmen. Da der Key bei 64 Zeichen abgeschnitten wird, sollte der zweite Eintrag die "Duplicate Key"-Exception triggern.

@ghost
Copy link

ghost commented Mar 30, 2013

Leo, ich habe sinngemäß gleiche Testcases als alter command-liner natürlich direkt im filesystem angelegt, mit Pfadlänger über 350 Zeichen (und Gleichaheit auf den ersten 350 Zeichen). Natürlich kracht es wie in einer unmodifizierten 3.0.6 so wie du schreibst. Ich habe aber meine Test in mit modifizierter tl_files (siehe meinen vorigen Eintrag) gemacht um nachzuweisen, dass das Problem so behebbar ist!!!

Inzwischen hast du ja in der 3.1.RC.1 path auf text gesetzt und den unique Index gelöscht (um so das Problem zu vermeiden). Durch letzteres werden allerdings die in #5572 beschriebenen Verdoppelungen von file entries nicht mehr geblockt. Ich plädiere immer noch für den in meinen Tests bestens funktionierenden unique key auf (pid, path) (wie immer man den multi-column key in DCA hinkriegt).

@leofeyer
Copy link
Member

Ich plädiere immer noch für den in meinen Tests bestens funktionierenden unique key auf (pid, path)

Dagegen habe ich ja auch gar nichts, außer das es eben bei mir nicht funktioniert hat. Aber ich versuche es gerne noch mal.

@ghost
Copy link

ghost commented Mar 30, 2013

Ah, dann sind wir auf einer Schiene. Welche mysql Version nutzt du?

Ich habe noch einen Webspace, auf dem eine alte 5.0.18 läuft (die es aber auch schon richtig machen sollte), werde ich mal testen.

@leofeyer
Copy link
Member

leofeyer commented Apr 3, 2013

Ein erneuter Test hat fehlerfrei funktioniert. Ich habe daher in 78ef640 den Unique-Index über pid und name gesetzt.

@leofeyer leofeyer closed this as completed Apr 3, 2013
@Samson1964
Copy link

Der neue Unique-Index scheint genau mein Problem beim Update von 3.0.6 auf 3.1.1 zu sein. Wenn der Unique-Index nur 64 Zeichen lang sein darf (bei mir wird er genau nach 64 Zeichen abgeschnitten), warum wird dann pid und name (gar 255 Zeichen!) zusammengelegt? Da ist doch klar, daß es früher oder später kracht, vor allen Dingen weil ja auch eine pid vielfach vorkommen kann.

Die Frage ist jetzt auch ob ich trotzdem die 3.1.1er so laufen lassen ohne das
ALTER TABLE tl_files ADD UNIQUE KEY pid_name (pid, name);
ausführen zu können.

@tristanlins
Copy link
Contributor

@leofeyer ich hatte das Problem jetzt auch. Sobald der Dateiname > 64 Zeichen ist und du mehrere Dateien hast, deren erste 64 Zeichen identisch sind kracht es.

@leofeyer leofeyer reopened this Jul 10, 2013
@razzio
Copy link

razzio commented Jul 10, 2013

@leofeyer Ich habe genau das gleiche Problem gerade bei einem meiner Kunden hier auch. Mehre Dateien mit gleichem, sehr langem Namen und am Ende nur durch _1 ... _5 unterschieden... :-(

Update von 3.0 auf 3.1.1

@leofeyer
Copy link
Member

Behoben in d0ee006.

@NinaG
Copy link

NinaG commented Aug 23, 2013

Leider tritt der Fehler auch mit diese Datei noch auf, wenn Kunden solche traumhaften Kandidaten in der Dateiverwaltung liegen haben:

XYZ-September-´09 (1).JPG
XYZ-September-´09 (2).JPG
XYZ-September-´09 (3).JPG

Dann knallt das Update in der install.php dennoch (obwohl ich die obige Datei ausgetauscht habe) weg:

Fatal error: Uncaught exception Exception with message Query error: Duplicate entry 'tl_files/XYZ/XYZ-September-' for key 'path' (INSERT INTO tl_files (pid, tstamp, name, type, path, extension, hash) VALUES (113, 1377268042, 'XYZ-September-�09 (2).JPG', 'file', 'tl_files/XYZ/XYZ-September-�09 (2).JPG', 'jpg', 'b5e66d5e796dfc478d0e5708c0381fcf')) thrown in system/modules/core/library/Contao/Database/Statement.php on line 346

#0 system/modules/core/library/Contao/Database/Statement.php(261): Contao\Database\Statement->query()
#1 system/modules/core/library/Contao/Database/Updater.php(547): Contao\Database\Statement->execute(113, 1377268042, 'XYZ-September-?09 ...', 'tl_files/XYZ/BB...', 'jpg', 'b5e66d5e796dfc4...')
#2 system/modules/core/library/Contao/Database/Updater.php(522): Contao\Database\Updater->scanUploadFolder('tl_files/XYZ', 113)
#3 contao/install.php(918): Contao\Database\Updater->scanUploadFolder()
#4 contao/install.php(530): InstallTool->update300()
#5 contao/install.php(181): InstallTool->runDatabaseUpdates()
#6 contao/install.php(971): InstallTool->run()
#7 {main}

@tristanlins
Copy link
Contributor

@NinaG das ist wieder ein anderes Problem, entstanden durch das "kaputte" Sonderzeichen. Ich vermute dass es sich dabei nicht um einen UTF-8 codierten Dateinamen handelt. Um den Key zu bestimmen, schneidet die DB vor dem ´ ab so dass die DB nur noch tl_files/XYZ/XYZ-September- anstatt tl_files/XYZ/XYZ-September-´09 (1).JPG sieht.
Das ist ein Encoding Problem das mit dem ursprünglichen nix zu tun hat, deshalb gehört das in ein eigenes Ticket.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

6 participants