IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

Effiziente Bereichs-Queries mit CouchDB/GeoCouch

Im Vergleich zu klassischen SQL-Datenbank erfordern NoSQL-Datenbank vor allem bei der Datenabfrage ein Umdenken. Im Falle von CouchDB lässt sich zwar mit View Collation schon einiges erreichen, allerdings bei weitem nicht alles. Auf eine solche Grenze bin ich gestoßen, als ich Zeiträume speichern und abfragen wollte, also Einträge die ein Start- und Enddatum besitzen. Anfragen auf diese Daten könnten nun zu einem fixen Zeitpunkt alle darin ablaufenden Einträge erfragen, oder ausgeweitet auf einen Zeitraum auflisten, welche Einträge innerhalb eines Zeitfensters liegen. All dies ist mit CouchDB nicht wirklich lösbar.

Einen kleinen Workaround bietet die Idee, die Zeitleiste zu segmentieren und immer dann für einen Eintrag einen Key zu emitten, wenn der Zeitraum des Eintrages innerhalb dieses Bereichs liegt. Eine solche Map-Funktion könnte wie folgt aussehen. Hierbei wird für einen Eintrag jeweils ab dem Beginn für alle 5 Minuten ein Schlüssel in den Index emittiert.

Dokumentaufbau:

{
   "_id": "s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de",
   "begin": "2010-08-05T09:11:52.156Z",
   "end": "2010-08-05T09:23:13.457Z"
}

Map-Funktion:

//length of time segment (here 5 min)
var periodLength = (60*5);

function(doc)
{
        if(doc.begin && doc.end)
        {
                //start and end time as UNIX timestamps (seconds, not milliseconds)
                var begin =  Math.round(new Date(doc.begin).getTime()/1000);
                var end =  Math.round(new Date(doc.end).getTime()/1000);

                //calculate first matching segment of period
                var p = (begin - (begin%periodLength));

                //emit key for each matching period
                while(p<=end)
                {
                        emit([p, doc._id], null);
                        p = p + periodLength;
                }
        }
}

Der resultierende View sieht dann in etwa so aus:

Oder als Abfrage:
http://localhost:5984/entries/_design/entries/_view/docsByPeriodList?startkey=[1280998800]&endkey=[1281000193,{}]

{"total_rows":3,"update_seq":2,"offset":0,"rows":[
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1280999400,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"]15,"value":null},
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1280999700,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"],"value":null},
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1281000000,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"],"value":null}
]}

Eine viel bessere Lösung bietet jedoch die CouchDB-Erweiterung GeoCouch. Dank des R-Trees lassen sich Bereichtsabfragen effizient durchführen. Da GeoCouch eigentlich für zweidimensionale Geokoordinaten gedacht, muss sie und die GeoJSON-Syntax etwas missbraucht werden. Anstatt einer WGS84-Koordinate emittieren wir einfach einen UNIX-Zeitstempel und lassen den anderen Grad leer:

function(doc)
{
        if(doc.begin && doc.end)
        {
                //start and end time as UNIX timestamps (seconds, not milliseconds)
                var begin =  Math.round(new Date(doc.begin).getTime()/1000);
                var end =  Math.round(new Date(doc.end).getTime()/1000);

                emit(
                        {
                                type: "Point",
                                bbox : [0,start,0,end]
                        }, null
                );
        }
}

So lassen sich nun effiziente Bereichsabfragen durchführen:
http://localhost:5984/entries/_design/entries/_spatial/entriesByPeriod?bbox=0,1280998800,0,1281000600

{"update_seq":16,"rows":[
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","bbox":[0,1280999512,0,1281000193],"value":null}
]}

Ein PubSubHubbub-Subscriber-Client für Java

Das Web ist mit HTTP fest an ein Client/Server-Modell gebunden und eine dadurch implizierte Asynchronität der Kommunikation. Requests können ausschließlich von Clients initiiert werden und immer von Servern in Form von Responses beantwortet. Ein solches Modell ist ausreichend für den Abruf von Informationen, setzt allerdings Schranken bezüglich anderer Interaktionsformen. Andere Protokolle wie XMPP, SIP oder auch Technologien wie nachrichtenbasierte Middlewaresysteme besitzen oft keine so deutliche Trennung zwischen Client/Server und erlauben weniger eingeschränkt die Kommunikation zwischen Knoten. Dadurch enstehen neben dem Request/Reply Muster weitere typischen Muster für den Austausch von Nachrichten. Ein Muster für die Benachrichtigung über Ereignisse ist das Publish/Subscribe Muster. Interessierte Knoten subskribieren sich für bestimmte Ereignisse und ereigniserzeugende Knoten publizieren diese.

Ein solches Kommunikationsmuster ist mit HTTP direkt nicht möglich, auch wenn es insbesondere für Feeds interessant wäre. Zwar bestehen mit Server Pushes / Long Polling oder dem aufkommenden WebSocket Standard vereinzelte Lösung für das prinzipielle Problem, dass HTTP keine serverinitiierte Kommunikation erlaubt, jedoch sind diese Insellösungen bisher kaum in der Breite verwendbar.

Google hat mit dem PubSubHubbub-Protokoll ein einfaches offenes Protokoll erschaffen, dass auf reinem HTTP basiert und ein solches Publish/Subscribe Muster unterstützt. Der Trick hierbei ist die Tatsache, dass alle beteiligten Knoten selbst sowohl Server wie auch Client sind und somit sowohl Requests empfangen wir auch versenden können.

Im Rahmen des diretto Projekts habe ich für unseren Client eine java-basierte Subscriber-Implementierung entwickelt. Als Feed kann jeder PubSubHubbub-fähige Atom-Feed benutzt werden. Im Falle einer Änderung des Feeds, zum Beispiel der Veröffentlichung eines neuen Eintrags, wird das “Delta” des Feeds, also der neue Teil an die Callback-Methode übergeben.

Subscriber subscriber = new SubscriberImpl("subscriber-host",8888);
Subscription subscription = subscriber.subscribe(URI.create("http://feed-host/my-push-enabled-feed.xml"));

subscription.setNotificationCallback(new NotificationCallback()
{

    @Override
    public void handle(SyndFeed feed)
    {
        //TODO: Do something more useful with the new entries
    	WireFeed inFeed = (WireFeed) feed.originalWireFeed();
    	if(inFeed instanceof Feed)
    	{
    		List<?> entries = ((Feed) inFeed).getEntries();
    		for (Object o : entries)
    		{
    			if(o instanceof Entry)
    			{
    				final Entry entry = (Entry) o;
    				System.out.println("New entry: "+entry.getId());
    			}
    		}
    	}
    }

} );

Der Client benutzt intern Rome für die Auswertung der Atom-Feeds und Jetty als leichtgewichtigen, internen Webserver. Der Subscriber muss übrigens für den Hub erreichbar sein, insofern sollte er an eine öffentliche IP und den angegebenen Port gebunden werden.

Projekt auf github: java-sub-pubsubhubbub

ioexception.de

Benjamin Erb [] studiert seit 2006 Medieninformatik und interessiert sich insbesondere für Java, Web-Technologien, Ubiquitous Computing, Cloud Computing, verteilte Systeme und Informationsdesign.


Raimar Wagner studiert seit 2005 Informatik mit Anwendungsfach Medizin und interessiert sich für C++ stl, boost & Qt Programmierung, Scientific Visualization, Computer Vision und parallele Rechenkonzepte.


David Langer studiert seit 2006 Medieninformatik und interessiert sich für Web-Entwicklung, jQuery, Business Process Management und Java.


Sebastian Schimmel studiert seit 2006 Informatik mit Anwendungsfach Medizin und interessiert sich für hardwarenahe Aspekte, Robotik, webOs, C/C++ und UNIX/Linux.


Timo Müller studiert seit 2006 Medieninformatik. Er interessiert sich allen voran für Mobile and Ubiquitous Computing, systemnahe Enwticklung und verteilte Systeme, sowie Computer Vision.


Achim Strauß studiert seit 2006 Medieninformatik. Seine Interessen liegen in Themen der Mensch-Computer Interaktion sowie Webentwicklung und UNIX/Linux.


Tobias Schlecht studiert seit 2006 Medieninformatik und interessiert sich vor allem für Software Engineering, Model Driven Architecture, Requirements Engineering, Usability Engineering, Web-Technologien, UML2 und Java.


Fabian Groh studiert seit 2006 Medieninformatik. Seine Interessengebiete sind Computer Graphics, Computer Vision, Computational Photography sowie Ubiquitos Computing.


Matthias Matousek studiert seit 2007 Medieninformatik und interessiert sich besonders für Skriptsprachen, Echtzeitsysteme und Kommunikation.


Michael Müller [] studiert seit 2009 Medieninformatik. Er interessiert sich vor allem für Web-Technologien, Ubiquitous Computing, User-Interfaces, UNIX und Creative Coding.


Falco Nogatz [] studiert seit 2010 Informatik mit Anwendungsfach Mathematik. Er interessiert sich für Web-Technologien, Programmierparadigmen und theoretische Grundlagen.

Archiv

August 2010
M D M D F S S
« Jul   Okt »
 1
2345678
9101112131415
16171819202122
23242526272829
3031