developer.studivz.net where the VZ developers blog

Archive for the ‘Javascript’ Category

Memcache Feeds

with 13 comments

Buschfunk, die Möglichkeit die Statusnachrichten (”Ist gerade…”) deiner Freunde auf unseren VZ’s anzuzeigen, ist nun/nur der Beginn der VZ Feeds. Letzlich ist der Buschfunk nur die Zusammenführung aller Statusnachrichten deiner Freunde auf der Startseite.

Nach dem Launch der ersten Version des Buschfunks, gab es bereits wenige Minuten später einen riesigen Impakt auf unserer Serverfarm. Dies führte dazu, dass wir bereits nach einem Tag die Statusnachrichtendatenbank auf eigene Server umziehen mussten. Ihr seht hier die absolute Anzahl von Statusaktualisierungen pro Minute (getrennt nach studiVZ/meinVZ = blau/flacher Graph und schuelerVZ = rot/steiler Graph):

zugriffe_mb2

Man kann Feeds, also Mitteilungen über Statusaktualisierungen eines Freundes, unterschiedlich implementieren und stellt sich dabei einigen Herausforderungen, gerade wenn es nicht nur um die optimale Speicherung, sondern auch um performante Zugriffe und logisches Zusammenführen von ähnlichen Feedeinträgen geht.

Wir haben uns für erste Tests in Richtung Social-Feeds für eine reine Memcache-Implementierung entschieden. Man hat den Vorteil, dass man die ohnehin nur momentan interessanten Posts von Usern nicht in der Datenbank vorhalten muss und Memcacheoperationen dazu noch um einiges schneller sind. Für einen Feed baut man sich im einfachsten Fall eine Queue pro User mit sortierten Einträgen auf, die man als Entity im Memcache ablegt. Da man nicht unendlich viele Einträge vorhalten muss und ein Memcacheobjekt per default eh nur 1 MB pro Eintrag groß sein darf, limitiert man die Queue auf eine feste Anzahl und wirft bei einem neuen Eintrag einfach alte Einträge weg.

Ok, genug der Einleitung, kommen wir zu ein paar Codeschnippseln. Am besten baut man sich ein Interface, welches sich um das Handling von Feedeinträgen kümmert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface FeedEntry {
 
//referenziertes Memcacheobject, also eigentlicher Inhalt
public function getFeedEntryReference(...);
 
//ist der Eintrag sichtbar -> Privacy
public function isFeedEntryHidden(...);
 
//initialer Aufbau
public function initializeFeedEntry(...);
 
[...]
 
}

Jetzt haben wir einen beliebig erweiterbaren Feed Typen und können mit Implementierungen, wie zum Beispiel Statusänderungen von Nutzerprofilen, Microblogeinträgen etc, beginnen.

Was man jetzt noch braucht, ist das eigentliche Aufbauen der Queue, also Füllen des Feeds mit Feed Typen. Man sollte sich überlegen, woraus ein Eintrag innerhalb der Queue aussehen soll. Es sollte die id des Users, einen Zeitstempel, sowie den eigentlichen Inhalt enthalten. Wir haben uns dafür entschieden, den Inhalt des Eintrages nur als Referenz in der Queue zu halten, damit bei Änderungen nicht jedes Queueobjekt, sondern lediglich das referenzierte Memcacheobjekt geändert werden muss. Ausserdem will man die Daten nicht doppelt im Speicher halten.

Beispielhaft könnte eine vereinfachte Queue folgendermaßen aussehen:

1
2
3
4
5
$queue = array(
0 => array ('timestamp' => time(), 'userId' => 123456789, 'contentId' => 1000, 'type' => TYPE_MICROBLOG),
1 => array ('timestamp' => time(), 'userId' => 1234567910, 'contentId' => 1001, 'type'  => TYPE_PHOTOCOMMENT)
[...]
);

Mit Hilfe des Typs und dessen konkreter Implementierung eines Feed Entries kann nun der eigentliche Inhalt aus einem weiteren Memcacheobjekt oder aus der Datenbank geholt werden. Sortiert ist die Queue bereits nach dem Einfügen eines neuen Entries. Wird nun der Content hinter einer solchen Referenz gelöscht, so braucht man die Queues der User, dessen Feeds beeinflusst werden, nicht updaten, da der Feed beim Einlesen automatisch merkt, dass die Referenz nicht mehr gültig ist und diese “überspringt”.

Für’s Aufbauen der Queue noch ein Tipp: Verwendet lieber mehrmaliges array_reverse im Zusammenhang mit array_push, anstatt ein array_shift! Das ist um Welten schneller, wenn man ein Element vorn ranhängt bzw. hinten anfügt.

(Quelle: http://www.ingo-schramm.de/blog/archives/9-PHP-array_shift-does-not-scale.html)

Da der Memcache, wenn der ihm zugewiesene Speicher vollläuft, wenig frequentierte Bereiche freigibt, muss man sich zwangsweise überlegen, wie man mit Datenverlusten innerhalb der Queue umgeht. Dazu könnte man zyklische Backups der Queueeinträge oder ein initiales Befüllen der Feedeinträge implementieren (im Interface bereits vorgesehen). Die eigentlichen Daten (bsp. das konkrete Statusupdate) bleiben natürlich erhalten und liegen in der Datenbank persistent vor, es geht hierbei nur um die Referenzen auf diese Einträge.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • VZ
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Technorati
  • TwitThis

A good piece of geek stuff – client side image processing with Gears

with 4 comments

In the latest downtime we released a beta version of a new photo uploader. Since we removed the Java based uploader some time ago, we’ve been dreaming of offering our users an uploader that’s able to do the same as the applet did, without requiring Java and of course with less conflicts on the different clients.

So the requirements for the uploader were :

  • uploading (of course :D)
  • scaling
  • rotating
  • compression

We implemented a Flash based uploader that could do a multiupload but neither scale nor compress the pictures before upload. So there is the problem, no direct access to the local files on the client.

On the Google Developer Days in Munich last year a collegue and I heard of the possibilities Gears offers and we were quite suprised how far it pushes the abilities of the client. Dreaming of all the geeky things I could do with Gears I also hoped to be able to solve that fileaccess problem. But unfortunately Gears did not offer the announced canvas API and also the desktop API hadn’t implemented the needed interfaces so far.

Enough story let’s look at the code …


var desktop = google.gears.factory.create('beta.desktop');
var localServer = google.gears.factory.create('beta.localserver');
var store = localServer.createStore('picturesTemp');

November 24, 2008: Google released the 0.5 version of Gears and there it was, the local server offered captureBlob() .

We created the needed Gears features, desktop for the fileaccess, localserver to store the files on the client.


$('#openFile').bind('click', function(){
gearsComponents_.desktop.openFiles(openFilesCallback_, {
filter: ['image/jpeg', 'image/png', 'image/bmp', 'image/gif']
});
});

Here we’re binding the filepicker dialog to some button, providing a filter to delimit the shown files to supported types.

var url = yourdomain;
var openFilesCallback_ = function(files){
var file = files_.shift();
gearsComponents_.pictureStorage.captureBlob(file.blob, url, "image/" + file.name.substring(file.name.lastIndexOf('.') + 1));
}

Capturing the blob to the local server like shown above solves two problems. Now we can access the files and import them into the canvas element, because it can be delivered via the same domain as the main page so there is no crossdomain security problem.

var canvas = $('<canvas>').get(0);
var context = canvas.getContext('2d');
canvas.width = canvasOriginal.width * fac;
canvas.height = canvasOriginal.height * fac;
context.scale(fac, fac);
context.drawImage(canvasOriginal, 0, 0);

In this example the canvasOriginal is the canvas/image from the localserver. We can now rotate, scale etc. the picture if the browser supports these actions on the canvas element.

You can also apply filters on the images by extracting the picture information as pixelarray modifying it and pushing it back:


// get the imagedata
var imgdata = canvasOriginalContext.getImageData(0, 0, canvasOriginal.width, canvasOriginal.height);
// do something with the pixel data
// push the imagedata back
context.putImageData(processedData, 0, 0);

As Javascript blocks while executing code this will for sure cause serious GUI problems, so let’s use a Gears worker to solve that problem.
First we have to create a workerpool:


var workerPool = (function(){
if (window.google) {
return google.gears.factory.create('beta.workerpool');
}
}());

The workerpool needs an onmessage handler which will be called on receiving messages by a childworker:


workerPool.onmessage = function(a, b, message){
//message will contain our processed pixelarray
};

The workers have no access to the dom so we only push the pixelarray in and get it back in the onmessage handler


// accessing the predefined workerpool
var script = 'var wp = google.gears.workerPool;' +
'wp.onmessage = function(a, b, message) {' +
'var data = message.body[0];' +
'//Process the data here' +
'//send the data back to the worker pool'
'wp.sendMessage(reply, message.sender);' +
// create a childworker by script (could also be created by url pointing to a script)
var childWorkerId = workerPool.createWorker(script);

We currently only use workers to apply a filter to the pictures.

Now we can do anything we want with the pictures but what about sending them to the server.

The solution to that problem is the toDataURL() method of the canvas element which exports the pictures to a “data URL”, which we can send to the server by a simple xhr as post.

According to the HTML5 spec toDataURL() should support several parameters first the data type  – e.g. toDataURL(’image/jpeg’) – the second parameter should be the compression rate as float value.

The compression rate does not seem to be supported by Firefox so far, so you should leave it blank then Firefox uses it’s default value.

The released beta version of the Gears uploader only supports Firefox because of the missing canvas (especially picture export) support in most of the browsers. Another drawback is that Gears is not available on all OS/browser combinations.

I hope that Safari will soon support (didn’t look at Safari 4 so far) the needed Canvas export methods, so that the uploader will work with Mac/Safari.

The CanvasAPI of Gears is already available in the sources but it’s not sure if and when it will be released. So perhaps some day there will be also a canvas element available in IE via Gears.

Have fun with the Gears uploader!

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • VZ
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Technorati
  • TwitThis

UTF-8 Byte Order Marks (BOMs) automatisch entfernen

with one comment

Insbesondere in heterogenen Netzwerken in einer Entwicklungsabteilung mit unterschiedlichster Softwarenutzung, kann es schon mal vorkommen, dass jemand einen Editor verwendet, der auf den falschen Zeichensatz eingestellt ist.

Ehe man sich versieht, wird aus dem ehemals unsichtbaren Byte-Order-Mark-Steuerzeichen, ein sichtbares Zeichen, das besonders am Anfang von .php  und JS-Dateien durchaus für Ungemach sorgen kann, wenn es vom Webbrowser falsch interpretiert wird.  Entweder nur hässlich sichtbar oder der Code funktioniert nicht mehr.

Bei uns prüft ein pre-commit-hook jeden SVN-Commit auf BOMs. Und für alle Homepagebastler die sich auch schon das eine oder andere mal darüber geärgert haben – mit der simplen – auf einer Shell eines Linux-Rechners ausgeführten Zeile wird man die Dinger komplett los. Wenn man SVN benutzt, kann man die Änderungen danach auch direkt committen, da die SVN-Steuerdateien ausgenommen sind.

find .  -name .svn -prune -o -type f -print  | while read file;do [ "`head -c3 -- "$file"`" == $'\xef\xbb\xbf' ] && sed -i -s -e '1s/^\xef\xbb\xbf//' $files $file && echo "found BOM in: $file";done

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • VZ
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Technorati
  • TwitThis

Written by timo February 10th, 2009 at 5:52 pm.


Posted in Javascript, PHP, Programming, QA

Tags: , ,

Alles neu macht der Mai, ähh Februar!

with 12 comments

greasemonkeyFrontend Refactoring macht Userstylesheets und Greasemonkey Scripts unbrauchbar.

Um zukünftig flexibler und effizienter arbeiten zu können, haben wir die Plattformen schülerVZ, studiVZ und meinVZ einem Frontend Refactoring unterzogen. Optisch gibt es nur wenige Änderungen, unter der Haube steckt aber eine neue Konstruktion. Im Rahmen des Refactorings werden wir jetzt nach und nach auch alle Seiten anpassen und optimieren.

Mitglieder unserer Plattformen, die die Oberfläche via Userstylesheets oder Greasemonkey Scripts anpassen, werden nach dem nächsten Release (Donnerstag, 5. Februar 2009) diese Skripte nicht mehr verwenden können. Wer seine Skripte bereits vor dem nächsten Release prüfen und anpassen möchte, kann sich hier eine Zip-Datei downloaden. In der Zip-Datei sind wichtige Seiten abgespeichert, sodass man seine Skripte entsprechend anpassen kann.

Wir hoffen, das hilft etwas.

Download Zip-Datei

Schöne Grüße aus dem Maschinenraum.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • VZ
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Technorati
  • TwitThis

Written by sebastian February 2nd, 2009 at 4:00 pm.


Posted in CSS, Javascript

Tags: , , ,