Opensuse 13.2, Subversion – sysconfig File "svnserve" fehlt

Opensuse 13.2 bietet einige Überaschungen und nicht immer positive. So wollte ich kürzlich auf einem neu installierten OS 13.2 einen einfachen Subversion-Server einrichten. Hierzu installierte ich mir die erforderlichen Pakete.

Natürlich unterliegt auch das Programm “svnserve” inzwischen der Kontrolle von systemd. Ein Startup-Skript unter “/etc/init.d” sollte also überflüssig sein und findet sich unter Opensuse 13.2 (im Gegensatz zu OS 13.1) auch nicht mehr.

Leider führte ein versuchsweises Absetzen des Kommandos

systemctl start svnserve.service”<&/p>
zu einem Fehler:

mytux:~ # systemctl start svnserve.service   
Job for svnserve.service failed. See "systemctl status svnserve.service" and "journalctl -xn" for details.
mytux:~ # systemctl status svnserve.service 
svnserve.service - Subversion protocol daemon
   Loaded: loaded (/usr/lib/systemd/system/svnserve.service; enabled)
   Active: failed (Result: resources) since Fri 2015-01-16 11:58:37 CET; 21s ago
  Process: 14949 ExecStart=/usr/bin/svnserve --daemon --pid-file=/var/run/svnserve/svnserve.pid $SVNSERVE_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 14950 (code=killed, signal=TERM)

Jan 16 11:54:03 rux svnserve[14949]: DIGEST-MD5 common mech free
Jan 16 11:58:42 rux systemd[1]: svnserve.service failed to run 'start' task: No such file or directory
Jan 16 11:58:42 rux systemd[1]: Failed to start Subversion protocol daemon.
mytux:~ #

Fragt sich, was das fehlende File ist. Dazu muss man in der schönen Welt von “systemd” etwas in der Tiefe graben. Die Antwort findet sich im Verzeichnis “/etc/systemd/system/multi-user.target.wants/”. Dort gibt es einen Link “svnserve.service”, der auf die Datei
“/usr/lib/systemd/system/svnserve.service” verweist. Inhalt:

[Unit]
Description=Subversion protocol daemon
After=syslog.target network.target

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/svnserve
User=svn
Group=svn
PIDFile=/var/run/svnserve/svnserve.pid
ExecStart=/usr/bin/svnserve --daemon --pid-file=/var/run/svnserve/svnserve.pid $SVNSERVE_OPTIONS

[Install]
WantedBy=multi-user.target

Aha! Es stellt sich also die Frage, ob es das EnvironmentFile, das ich von früheren Opensuse-Versionen sehr gut kenne, im “/etc/sysconfig”-Bereich überhaupt noch findet? Antwort: Leider nein!

Da stimmt also womöglich was nicht mit dem Subversion-Paket. Ein Deinstallieren und Neuinstallieren half aber leider nichts. Eine anschließende Suche im gesamten Filesystem zeigte, das eine geeignete Konfigurationsdatei auch sonst nirgends zu finden war/ist.

Was also tun? Ich habe mir dann einfach ein entsprechendes sysconfig-File “svnserve” aus einer früheren Opensuse 13.1-Installation in das Verzeichnis “/etc/sysconfig” kopiert. In der nicht ganz unberechtigten Annahme, dass das File hoffentlich auch für systemd auswertbar sein würde, wenn es in der systemd-Konfiguration dort als “EnvironmentFile” erwartet wird. Inhalt:

## Path:	/etc/sysconfig/svnserve
## Description:	Basic configuration for svnserve
## Type:	string
## Default	"-d -R -r /srv/svn/repos"
#
# Default options for the svnserve process.
# The -R option enforces read-only access, i.e. write operations to the
# repository (such as commits) will not be allowed.
# Authentication should be configured before allowing write access.
# See http://svnbook.red-bean.com/en/1.8/svn.serverconfig.svnserve.html#svn.serverconfig.svnserve.auth
#
SVNSERVE_OPTIONS="-d -r /projekte/SVNserv"

## Type:	string
## Default	"svn"
#
# svnserve should 
run as unprivileged user.
# If you want to expose the repository via both svnserve and mod_dav_svn
# (Apache httpd) in parallel, ensure that the apache user is part of the
# svn group and the setgid flag is set on the repositories
# usermod -A svn wwwrun
# chmod -R g+s /srv/svn/repos
# See http://svnbook.red-bean.com/en/1.8/svn.serverconfig.multimethod.html
#
SVNSERVE_USERID="svn"

## Type:	string
## Default	"svn"
#
# svnserve should run as unprivileged user.
# If you want to expose the repository via both svnserve and mod_dav_svn
# (Apache httpd) in parallel, ensure that the apache user is part of the
# svn group and the setgid flag is set on the repositories
# usermod -A svn wwwrun
# chmod -R g+s /srv/svn/repos
# See http://svnbook.red-bean.com/en/1.8/svn.serverconfig.multimethod.html
#
SVNSERVE_GROUPID="svn"

Die Directory-Angabe am Ende der Variablen SVNSERVE_OPTIONS bezieht sich auf das Haupt-Repository, das ich auf besagtem System unter “/projekte” angelegt hatte. Diese Angabe muss man natürlich an eigene Verhältnisse anpassen. [Standard wäre unter Opensuse ein Repository namens “svn” im Verzeichnis “/srv/svn/”.]

Und siehe da:

mytux:~ # systemctl status svnserve.service 
svnserve.service - Subversion protocol daemon
   Loaded: loaded (/usr/lib/systemd/system/svnserve.service; enabled)
   Active: active (running) since Fri 2015-01-16 12:00:12 CET; 1h 18min ago
  Process: 15198 ExecStart=/usr/bin/svnserve --daemon --pid-file=/var/run/svnserve/svnserve.pid $SVNSERVE_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 15199 (svnserve)
   CGroup: /system.slice/svnserve.service
           └─15199 /usr/bin/svnserve --daemon --pid-file=/var/run/svnserve/svnserve.pid -d -r /projekte/SVNserv

Jan 16 12:00:12 rux svnserve[15198]: DIGEST-MD5 common mech free

Hier erkennt man auch, dass systemd beim Start von “svnserve” den Inhalt der Variable SVNSERVE_OPTIONS aus dem EnvironmentFile zieht.

Ich hoffe, das hilft dem einen oder anderen OS 13.2-Umsteiger/Einsteiger weiter.

MySQL – SELECT INTO OUTPUT FILE – vermeide zusätzliche UNION Statements!

Wenn es um den Export relativ großer Tabellen in CSV-Dateien geht, greift man bei MySQL-Datenbanken auf eine spezielle Form des SELECT Statements, nämlich “SELECT INTO OUTPUT FILE … “. Ich nenne das nachfolgend der Einfachheit halber ein “Export SELECT”. Siehe hierzu:
http://dev.mysql.com/doc/refman/5.7/en/select-into.html

Oftmals benötigt man aber nicht nur die reinen Daten sondern eine CSV-Datei mit einer führenden Zeile, in der die Bezeichnungen der einzelnen Felder/Spalten auftauchen (“column heading”). Die gängige Empfehlung vieler Beiträge zu diesem Thema ist, das Ergebnis des oben genannten Statements “SELECT INTO OUTPUT FILE … ” per UNION mit einem “künstlichen SELECT-Statement zu verbinden, dass genau die Strings der ersten Zeile erzeugt. Siehe hierzu etwa
http://stackoverflow.com/questions/356578/how-to-output-mysql-query-results-in-csv-format (und dort einen entsprechenden Kommentar zum Lösungsansatz der Forums-Frage)
oder aber
http://www.mysqltutorial.org/mysql-export-table-to-csv/
und dort der Abschnitt zu “Export Data with Column Headings”.

Entsprechende Vorschläge folgen dem Muster A

(SELECT 'Name of Column 1', 'Name of Column 2', 'Name of Column 3')
UNION
(SELECT column_name_1, column_name_2, column_name_3
FROM TABLE_NAME
INTO OUTFILE 'absolute_path_to_csv_file'
FIELDS ENCLOSED BY '"' TERMINATED BY ';' ESCAPED BY '"'
LINES TERMINATED BY '\r\n');

Festzustellen ist hierzu Folgendes:

Ein solches Statement funktioniert zwar und produziert das gewünschte Ergebnis; aber es reduziert die Performance des Daten-Exports in eine Datei drastisch – speziell wenn man mit großen Tabellen und mehreren Millionenen oder gar Hunderten von Millionen Records arbeitet. Dann entfaltet sich das UNION-Statement zu einem echten Performance-Killer!

In einem konkreten Testbeispiel konnte ich den Export aus einer Datei mit einer Million Records vergleichen. Der Export mit einem Union Statement dauerte fast 50 Sekunden; läßt man die Kopfzeile dagegen nach dem Muster B

SELECT column_name_1, column_name_2, column_name_3
FROM TABLE_NAME
INTO OUTFILE 'absolute_path_to_csv_file'
FIELDS ENCLOSED BY '"' TERMINATED BY ';' ESCAPED BY '"'
LINES TERMINATED BY '\r\n';

weg, so dauerte der Export nur 1,8 Sekunden. Das Verhältnis wird noch schlimmer bei größeren Record-Zahlen der Tabelle.

Ein wenig tieferes Nachdenken und eine SQL-Analyse mit “Explain” machen auch klar, woran das liegt: Der Einsatz von UNIONS erfordert das Zusammenfügen der Resultsets der verschiedenen SELECTs. Dazu muss jedes Resultset aber erstmal generiert werden. Dazu müssen temporäre Datenhaltungsstrukturen aufgebaut werden. Dies kollidiert jedoch mit der ansonsten speziell optimierten Funktionalität des “SELECT INTO OUTPUT FILE”-Statements, nämlich die Tabelleninhalte aus den Datenbankfiles mit speziellen Methoden direkt auszulesen und sofort in eine Datei zu schreiben.

Ich kann daher nur jedem raten, beim Export wirklich großer Tabellen in CSV-Dateien auf jegliche Kombination des “SELECT INTO OUTPUT FILE”-Statements mit anderen SELECTS unter Rückgriff auf “UNION” zu verzichten. Das zieht ggf. den Verzicht auf eine erste führende Zeile nach sich.

Änderung und Nachtrag 08.01.2015:
In Fällen mit kleineren Dateien von bis zu n x 100 MByte kann es – je nach RAM-Ausstattung des Systems
– u.U. besser sein, zunächst den reinen Daten-Export (gem. Muster B) durchzuführen und danach die erzeugte Datei mit PHP File-Funktionen oder Systemfunktionen nachzueditieren und die gewünschte erste Zeile mit der Feldinformation hinzuzufügen. Das ist aber zu testen. In meinem Testfall mit 1 Million Datensätzen war das File kleiner als 50 MByte und lag auf einer SSD. Zudem ist das System hinsichtlich der Plattenzugriffe gut gepuffert. In diesem Fall erhöhte sich die benötigte Zeit durch das Hinzufügen der Kopfzeile untr PHP kaum spür- und messbar über die 1,8 Sekunden hinaus. Die Änderung war gegenüber dem oben erwähnten Zeitunterschied des UNION-Statements völlig vernachlässigbar.

Bei Dateigrößen von mehreren hundert Mbyte oder gar im GByte-Bereich wird ein nachträgliches Editieren unter PHP aber ebenfalls zu Problemen führen, da das File erst einmal zu lesen und dann mit dem String für die erste Zeil zu konkatenieren ist. Man muss in Tests prüfen,

  • ob das für solche Operationen erlaubte Memory unter PHP hinreichend ist (und es ggf. anpassen)
  • und ob sich die Lesezeit unter PHP als kleiner oder größer als die von MySQL aufgebrachte Zeit für ein UNION herausstellt.

Am besten erscheint es mir, bei wirklich großen Datenmengen im Sinne der Performance schlicht und einfach auf die führende Kopfzeile zu verzichten. Derjenige, der das exportierte CSV-File in irgendeiner Weise nutzen will, muss eben über die Feldreihenfolge Bescheid wissen.

PHP Code Content Assists und Inline Type Hinting in Eclipse

Vor ein paar Jahren war ich sehr happy, als ich entdeckte, dass Eclipse mit dem PHP Pluging (PDT) etliche Möglichkeiten im Bereich des PHP Content Assistings bietet. So nutze ich die Möglichkeiten des Type Hintings mittels “phpDocumentor tags” ausgiebig oberhalb der Deklaration von Class Member Variablen oder bei der Definition von Funktionen (frei oder als Objekt-Methoden). Es ist einfach schön, bequem und sehr effizient, wenn “Ctrl-Space” das Tippen von neuem Code in einem PHP-Projekt mit passenden Vorschlägen aufgrund der Definition von Klassen etc. in anderen Dateien unterstützt. Fast magisch !

Was ich bisher nicht hinbekommen hatte, war das Type Hinting und “Content Assisting” zu Variablen im PHP-Code, die im Rahmen einer dynamischen Namensgebung über die Nutzung der Funktion “call_user_func” als Objekt einer Klasse instanziiert werden. Das braucht man z.B., wenn man das Singleton-Pattern benutzt und das Singleton-Objekt entweder erstmalig instanziiert werden muss oder aber die Referenz auf das ggf. schon existierende Singleton-Objekt geholt werden soll – soweit es bereits woanders instanziiert wurde.

Aber auch das geht unter Eclipse PDT mit sog. “Inline Type Hinting”. Ein Beispiel:

	$get_instance ="getInstance"; 
	$Class_SS_Run_Data_Name = "Class_SS_Run_Data";
	$ay_SS_Run_Data		= array($Class_SS_Run_Data_Name, $get_instance);

	/* @var $SS_Run_Data Class_SS_Run_Data */		
	$SS_Run_Data = call_user_func($ay_SS_Run_Data);
	unset($ay_SS_Run_Data); 		

getInstance ist hier eine statische Methode, die gemäß des Singleton Patterns entweder das Objekt instanziiert oder die Referenz auf das Singleton Objekt aus einer statischen Variablen ermittelt.

Wichtig ist hier aber die Kommentar-Zeile und ihr Aufbau. Die Klasse “Class_SS_Run_Data” ist in einer anderen Datei definiert, deren Source Pfad natürlich im Bereich des BUILD Path des PHP-Projektes liegen muss.

Tippe ich nun später “$SS_Run_Data->” im Code erhalte ich je nach Einstellungen des Content Assistings umgehend oder spätestens über Ctrl-Space eine Vorschlagsliste zu Variablen oder Methoden der Klasse. Super !

Gelernt habe ich das nach Lesen eines Artikels von Norm McGarry, der sich mal die Mühe gemacht hat, die verschiedenen Arten des “Type Hintings” für “Content Assist”-Zwecke unter Eclipse PDT zusammenzustellen. Siehe:
PHP Type Hinting with Eclipse

Herzlichen Dank an Norm McGarry!