tisdag 10 augusti 2010

Unspecified Error vid användning av pasteHtml i Internet Explorer

Eftersom Internet Explorer saknar stöd för execCommand med parametern "insertHtml" så får man använda sig av pasteHtml istället.

var html = "test";
if (document.selection && document.selection.createRange) {
   var range = document.selection.createRange();
   if (range.pasteHTML) {
      range.pasteHTML(html);
   }
}else{
   document.execCommand("insertHtml", false, html);    
}

Första gången jag använde metoden ovan så fungerade det perfekt. Men idag när jag skulle göra samma sak så fick jag ett "Unspecified error" i Internet Explorer. Inte helt oväntat så är det lite svårt att felsöka ett "Unspecified error". Jag har ägnat halva dagen åt att googla problemet utan resultat. Till slut kom jag fram till att problemet berodde på att man måste ge det aktuella fönstret fokus. I detta fall så gällde det en iframe där jag ville klistra in kod så där såg lösningen ut såhär:

document.getElementById("iframeId").contentWindow.focus();

Förhoppningsvis hittar ni denna lösning i google nu :)

tisdag 8 juni 2010

Kör maskinkod i användarens webbläsare med Native Client (NaCl)

Även om javascript bara blir snabbare och snabbare så ligger det ganska långt ifrån hastigheten på riktig maskinkod. Därför har Google startat ett projekt som heter Native Client (NaCl) som gör att man från javascript kan ladda ned och exekvera speciella exe-filer i användarens webbläsare.

Det låter kanske lite läskigt men det verkar finnas ett väl genomarbetat säkerhetslager som kontrollerar att ingen farlig kod kan köras, man kommer t.ex. såklart inte åt användarens filsystem. Säkerhetskraven påverkar hastigheten något men det är ändå nästan lika snabbt som att köra ett vanligt program.

En väldigt positiv sak är att man kan skriva sina spel eller beräkningstunga applikationer i vilket språk man vill så länge det har en NaCl-kompilator.

Tyvärr är NaCl än så länge på ett tidigt experimentstadium men jag hoppas vi får se stöd för detta i alla browser inom kort.



Mer info:
http://code.google.com/events/io/2010/sessions/native-code-chrome.html
http://code.google.com/p/nativeclient/

tisdag 1 juni 2010

Google dödar long-tail-trafiken

Google har i början av maj gjort förändringar i sin algoritm som gör att undersidor på en sajt som inte har så många inlänkar inte längre rankar lika högt. Med andra ord kommer t.ex. permalänkar på bloggar att hamna betydligt lägre när bloggposterna blir äldre.

WebmastersWorld kallar denna uppdatering "Mayday" och google har bekräftat den, Matt Cuts säger:

This is an algorithmic change in Google, looking for higher quality sites to surface for long tail queries. It went through vigorous testing and isn’t going to be rolled back.

Har du en blogg eller e-handelssajt har du förmodligen märkt av en kraftig minskning av trafik den senaste månaden som kanske inte enbart har orsakats av bra väder och förmodligen kommer att vara permanent.

Det enda råd man kan ge är förmodligen att man bör försöka göra sig så oberoende av google-trafik som möjligt och satsa på att ge läsarna bra innehåll och en bra upplevelse snarare än att försöka optimera efter googles algoritm eftersom den ständigt förändras.

Källor:
http://www.webmasterworld.com/google/4125460.htm
http://searchengineland.com/google-confirms-mayday-update-impacts-long-tail-traffic-43054

måndag 31 maj 2010

Ladda php-klasser först när de ska användas

Idag var jag på Optimera STHLM och lyssnade på flera intressanta föreläsningar. Det var inte jättemycket nytt under solen men inspirerade till att optimera sin kod än mer och kändes som ett väldigt bra initiativ av IIS.

En oväntad sak jag lärde mig var att php har en inbyggd funktion som talar om hur mycket minne ens php-script allokerat som mest under exekveringen, nämligen: memory_get_peak_usage()

Jag var såklart tvungen att testa denna funktion på Pusha där jag blev glatt överraskad av att Pusha som mest verkar använda 4mb minne. Till min förvåning visade det sig att Qvaq använde mer minne än vad Pusha gör vilket var väldigt märkligt eftersom Pusha består av betydligt fler klasser. Efter ett tag insåg jag dock att det beror på att vi på Pusha laddar in endast de klasser som verkligen används dynamiskt istället för att ladda alla klasser hela tiden.

I vanliga fall kanske ens php-kod innehåller ett stort antal include-satser för att ladda in alla klasser man behöver, det kan se ut ungefär såhär (fast med hundratals rader):

include "classes/User.php";
include "classes/Link.php";
include "classes/Comment.php";

För att istället ladda in en klass först när den behövs om den behövs över huvud taget så behöver man bara override:a funktionen __autoload().

Denna funktion anropas automatiskt i php så fort php stöter på en klass som den inte känner till och skickar med namnet på den saknade klassen som inparameter. Det enda du behöver göra i funktionen är alltså att inkludera den fil där klassen som saknas är definierad. Enklast tänkbara sätt att göra detta är såhär:

function __autoload($class) {
   include "classes/".$class.".php";
}

På Qvaq sänkte dessa förändringar minnesanvändningen med 0,5mb per sidladdning från 4,25mb till 3,75mb. Kanske ingen jättevinst men då innehåller inte Qvaq speciellt många klasser och har man många requests kan det bli en hel del sparat minne.

Jag misstänkte att detta sätt att ladda in klasserna on-the-fly skulle göra att koden exekverades långsammare men enligt mina mätningar gick det lika snabbt eller möjligen till och med något snabbare.

torsdag 20 maj 2010

Tiodubbla hastigheten på dina InnoDB-tabeller

Vi har precis fixat en ny databas-server till Pusha och inatt satt jag uppe och försökte optimera inställningarna för MySQL. En förändring jag gjorde av standardinställningarna som gav oväntat bra effekt var att ändra innodb_flush_log_at_trx_commit. Denna variabel talar om för mysql hur ofta den ska skriva till loggen när man skriver till en innodb-tabell.

Standardvärdet för innodb_flush_log_at_trx_commit är 1 vilket betyder att varenda uppdatering måste flushas till loggen direkt. Bygger man en bank så är nog detta att rekommendera men för de flesta applikationer som inte kräver extremt hög säkerhet så räcker nog värde 2 som innebär att man bara skriver till loggen en gång per sekund. Skulle operativsystemet krascha så förlorar man alltså maximalt en sekunds ändringar.

Du ändrar värdet genom att lägga till denna rad under [mysqld] i /etc/mysql/my.cnf (eller var du nu har my.cnf):

innodb_flush_log_at_trx_commit  = 2

Glöm inte att starta om mysql för att ändringen ska få effekt.

Jag har inte gjort några exakta mätningar på hur stor skillnad detta gör men det blev synbart snabbare att pusha en länk på Pusha så det är minst 10 ggr så snabbt som med värdet 1.

MySQL-dokumentationen för innodb_flush_log_at_trx_commit
Fler tips på hur man finjusterar MySQL

Spotify rekryterar med programmeringstävling

Vill du ha ett jobb på spotify så ska du boka in lördag den 29e maj i kalendern. Spotify anordnar då en programmeringstävling där man kan vinna (än så länge ospecifierade) priser och goda möjligheter till en anställning i företaget.

På 4 timmar ska man lösa 6-8 uppgifter av en algoritmisk karaktär. Lösningarna ska skrivas i C, C++ eller Java och kommer att rättas av en dator.

Mer information och anmälan

Google lanserar Google Storage for Developers

Google har lanserat en tjänst för att lagra data i molnet - Google Storage for Developers. Egentligen en ren och skär kopia av Amazon S3. Även prissättningen är i princip densamma som för S3, google storage är något dyrare men å andra sidan får man nu under deras "Preview" 100 gigabyte lagringsutrymme och 300 gigabyte bandbredd per månad gratis.

Än så länge är tjänsten bara öppen för den amerikanska marknaden men förmodligen kommer det till Europa också så småningom.

Det finns en sak som skulle kunna få mig att svika Amazon S3 och övergå till Google Storage. Om Google Storage tillåter att man döper objekt till en tom sträng så att man kan lägga in en "förstasida" för sin bucket så är jag såld. Tyvärr har jag inte lyckats utreda om det är så eller ej.

torsdag 22 april 2010

Världsbankens data nu tillgänglig för alla

Världsbanken som tidigare tagit betalt för sin data har nu bestämt sig för att de vill dela med sig av denna data gratis till alla och gör det på ett väldigt snyggt sätt på http://data.worldbank.org/.



Det kan mycket väl hända att vi till stor del har den fantastiska svensken Hans Rosling att tacka för detta. Han har under lång tid och i ganska hårda ordalag kritiserat världsbanken och dess chef Robert B Zoellick för att de inte delat med sig av datat. Rosling har bland annat sagt:

"Det är underligt, på Pennsylvaniaavenyn i Washington finns två viktiga institutioner, Vita huset och Världsbanken. I den ena finns en ung, vald president och i den andra finns en gammal, tillsatt president. Den valda presidenten lärde sig genast använda internet och öppnade efter att ha valts sajten data.gov med fri tillgång till handlingar. Men den gamla presidenten säljer fortfarande sina data. Varför kan han inte promenera de 400 meterna till Vita huset för att lära av den unga presidenten?"

Rosling omnämns även i Världsbankens pressmeddelande om att de öppnat upp datat.

För dig som utvecklare är det såklart främst API:et som är intressant. Du hittar API-dokumentationen här: http://data.worldbank.org/node/10

DN: Världsbanken släpper alla siffror fria

Facebook dödar Facebook Connect

Facebook avlöjade på F8-konferensen igår sitt nya Open Graph API.

För utvecklare innebär det att det blir ett mer enhetligt api där man enkelt kan se hur olika facebook-objekt som användare, händelser, bilder o.s.v. hänger ihop.

Vad gäller autentisering så kommer man på sikt att fasa ut Facebook Connect och istället ersätta det med OAuth 2.0 (som twitter använder). Det är positivt på så sätt att OAuth är enklare och mer standardiserat än det virrvarr av javascript-requests som Connect består av. I samband med detta har man även gjort så att användaren bara behöver godkänna applikationen en gång istället för att behöva godkänna varje rättighet för sig som det är idag. Man skippar även de tidigare reglerna om att data bara får cacheas i 24 timmar vilket gör det betydligt enklare för oss utvecklare men kanske inte är helt bra ur användarnas synvinkel.

Facebook har en historia av att vara väldigt radikala när de gör förändringar i sina api:er. Ofta tar de bort stöd för metoder samma dag som en inför en ersättning. Förhoppningsvis kommer de inte vara lika hårda denna gång, det vore tråkigt om de förstörde några miljoner Connect-sajter över en natt. Jag hoppas även att de tänkt igenom detta nya API ordentligt så vi slipper fler förändringar som gör att alla applikationer måste skrivas om från grunden.

Graph API
Authentication

måndag 19 april 2010

Opacity i Internet Explorer

I CSS3 finns egenskapen opacity för att bestämma ett lagers transparens. Denna egenskap fungerar både i senare versioner av Firefox och Chrome. I Internet Explorer har man länge kunnat göra samma sak med filter: alpha(opacity = 50);. Detta har dock inte fungerat för lager som är relativt positionerade (vilket är nästan alla lager).

Idag hittade jag dock en lösning på detta. Om man sätter zoom: 1; så börjar filtret fungera även för relativt positionerade lager. Så ett crossbrowser-säkert sätt att göra ett lager halvtransparent är detta:

.halvtransparent {
   opacity: 0.5;
   -moz-opacity:0.5;
   filter: alpha(opacity = 50);
   zoom: 1;
}

onsdag 17 mars 2010

Gzip-komprimering på Amazon S3

Varje dag går världen miste om 99 år på grund av icke-komprimerat innehåll. Genom att gzippa dina html, css och javascript-filer m.m. kan många millisekunder sparas vid varje sidladdning samtidigt som du sparar bandbredd. Nästan alla webbservrar gzippar automatiskt innehåll om klienten berättar att den stöder gzip-kompression. Tyvärr gör inte Amazon S3 detta automatiskt.

Även om inte S3 automatiskt skickar gzippade versioner av dina filer när klienten stöder gzip så kan du självklart lagra gzippade filer på S3. Du kan alltså komprimera t.ex. din css-fil lokalt och sedan ladda upp den till S3. Såhär gzippar du en fil i linux:

gzip -c  plain_text.txt > compressed.txt.gz

Detta löser dock inte problemet helt för alla klienter stöder inte gzip och kommer alltså inte kunna läsa din komprimerade css-fil och därmed inte rendera din sajt korrekt.

Den gyllene medelvägen blir att lagra både en okomprimerad och en komprimerad version på S3 med bara en liten skillnad i namnet, t.ex. style.css och style.gz.css.

Sedan kan man med php eller något annat serverspråk enkelt avgöra om klienten har stöd för gzip och i så fall välja att ladda den gzippade versionen. Här är ett exempel på hur man tar reda på detta i php:

<?php
$gz = (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false)? 'gz.' : '';
?>
<link rel="stylesheet" type="text/css" href="http://s.example.com/style.<?=$gz?>css" />

När du laddar upp den gzippade filen till S3 bör den ha "Content-Encoding: gzip" i sin header. Annars kommer inte webbläsarna förstå att innehållet är gzippat utan försöka läsa det som vanlig text.

Pusha minskade vi på detta sätt storleken på vår css med 78% och javascriptet med nästan lika mycket.

fredag 5 mars 2010

Kom igång med XML-RPC och Loopias API

UPPDATERING: Efter att jag skrivit detta inlägg upptäckte jag att Erik Petterson har skrivit en wrapper-klass till Loopias API så att man slipper tänka på XML-RPC. Du hittar phpLoopia här.

Loopia har släppt ett API som följer XML-RPC-protokollet. Detta är första gången jag använder ett XML-RPC-api, nästan alla web-API:er man stöter på idag är av typen REST. Därför tänkte jag skriva ned en snabb guide till hur man kommer igång med XML-RPC i PHP.

För att komma åt Loopias API så måste du ha ett konto hos Loopia. Om du har det kan du skapa en API-användare under Kontoinställningar > LoopiaAPI.

Vilka metoder som finns i API:t kan du se på https://www.loopia.se/api. Den metod jag tänkte att vi skulle testa är domainIsFree(username,password,domain) som låter oss kontrollera om ett domännamn är ledigt eller upptaget.

XML-RPC är inte inbyggt i PHP som standard så för att skapa en XML-RPC klient måste vi börja med att ladda ned ett bibliotek från phpxmlrpc.sourceforge.net. Ladda ned 3.0.0Beta och packa upp. Filen vi behöver ligger i "lib" katalogen och heter xmlrpc.inc.

include "xmlrpc.inc";

$username = 'användare@loopiaapi';
$password = 'MittHemligaLösenord';

$domain = "kodkodkod.se";

$xmlrpcClient = new xmlrpc_client('RPCSERV','api.loopia.se',443);
$xmlrpcMsg = new xmlrpcmsg('domainIsFree',
                           array(new xmlrpcval($username,'string'),
                                 new xmlrpcval($password,'string'),
                                 new xmlrpcval($domain,'string')));

$xmlrpcResponse = $xmlrpcClient->send($xmlrpcMsg,0,'https');

if (!$xmlrpcResponse->faultCode()) { 
   if ($xmlrpcResponse->value()->scalarVal()=="OK"){
      echo "Domänen ".$domain." är ledig";
   }else if($xmlrpcResponse->value()->scalarVal()=="DOMAIN_OCCUPIED"){
      echo "Domänen ".$domain." är upptagen";
   }else{
      echo "Ett oväntat fel inträffade";
   }
}

Koden är förhoppningsvis självförklarande. Vi skapar ett klientobjekt och vi skapar ett xmlrpc-meddelande (ett anrop till domainIsFree) som vi skickar till servern med klienten. Om vi inte får en felkod till svar så kollar vi om svaret är "OK" eller "DOMAIN_OCCUPIED".

Dokumentationen till XML-RPC for PHP hittar du här.

Om du bygger något med Loopias API har du just nu chansen att vinna en iPad.

tisdag 2 mars 2010

Anpassa koden för cacheminnen

Ska redovisa en gammal labb-rest i en kurs i Datorteknik på fredag. Labben handlar om cacheminnen så jag är ganska insnöad på det ämnet just nu.

Något jag lärt mig av labben är att väldigt små skillnader i kod kan ge enorma skillnader i körtid. Jämför t.ex. dessa två olika sätt att addera två matriser:

for(j=0; j < MATRIX_SIZE; ++j){
   for(i=0; i < MATRIX_SIZE; ++i){
      res[i][j] = a[i][j]+b[i][j];
   }
}

for(i=0; i < MATRIX_SIZE; ++i){
   for(j=0; j < MATRIX_SIZE; ++j){
      res[i][j] = a[i][j]+b[i][j];
   }
}

Enda skillnaden här är att exempel 1 loopar igenom matriserna kolumn för kolumn medan man i exempel 2 går igenom matriserna rad för rad. Båda exemplen ger exakt samma resultat men exempel nummer 2 är överlägset snabbare med vissa cacheminnen. Detta beror på att matriser lagras radvis i RAM-minnet (enligt C-standarden och många andra språkdefinitioner). När ett program refererar till en variabel som inte finns i cacheminnet så hämtas variabeln in från RAM-minnet. Dessutom hämtas närliggande data in eftersom man förmodligen kommer vilja använda närliggande data. I exempel 2 tjänar man mycket på detta eftersom man där läser i den ordning som matriserna ligger lagrade och alltså förhoppningsvis får ganska många träffar efter varje miss. I exempel nummer 1 kan det extra datat som hämtats in ha hunnit skrivas över innan man får användning av det.

Jag kodade ett snabbt test i Java där jag adderar två matriser i storleken 1000x1000 på de två olika sätten. Här är exekveringstiderna:

radvis  kolumnvis
8.0ms   27.0ms
8.0ms   30.0ms
8.0ms   27.0ms
10.0ms  28.0ms
8.0ms   28.0ms
9.0ms   27.0ms
8.0ms   29.0ms
8.0ms   27.0ms
9.0ms   27.0ms
8.0ms   27.0ms
--------------------------------
8.4ms   27.7ms

Att loopa igenom matriserna radvis är alltså i snitt mer än 3 gånger så snabbt som att göra det kolumnvis.

För den som vill lära sig mer har KTH en omfattande PDF om Cacheminnen och adressöversättning.

Posta på Twitter med PHP

Om du vill göra en statusuppdatering på twitter med php så går det alldeles utmärkt med Twitters REST API.

Här är en enkel funktion som använder cURL i php:

<?php
function tweet($twitterUser,$twitterPass,$tweet){
   $url = "http://twitter.com/statuses/update.xml";
   $data = "status=".urlencode(utf8_encode($tweet));
   $ch = curl_init($url);
   curl_setopt($ch, CURLOPT_POST, 1);
   curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
   curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
   curl_setopt($ch, CURLOPT_HEADER, 0);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($ch, CURLOPT_USERPWD, $twitterUser.":".$twitterPass);
   curl_exec($ch);
   $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
   curl_close($ch);
   if ($httpcode == 200) {
     return true;
   }else{
     return false;
   }
}
?>

Och du anropar funktionen såhär:

<?php
   tweet("MittTwitterNamn",
         "MittHemligaTwitterLösen",
         "Missa inte mitt inlägg på http://kodkodkod.se");
?>

Om du inte har stöd för cURL på ditt webbhotell borde det vara ganska enkelt att skriva om funktionen så att den använder fopen istället.

Pusha använder vi detta sätt för att posta heta länkar till @PushaHett på twitter. Observera att denna funktion är lämpligast att använda om du vill posta statusuppdateringar på konton som du ansvarar för. Om du vill låta dina användare posta statusuppdateringar på sina egna twittersidor från din sajt bör du implementera OAuth istället för att fråga användarna efter deras lösenord.