Författararkiv: Jimmy Bergman

Lovtal till .SE

Vi på Loopia vill ta tillfället i akt att framföra vår fullkomliga tillfredsställelse med .SEs professionellt genomförda övergång till en ny och för alla parter fördelaktig affärsmodell.

Loopia och .SE har haft ett långt och lyckat samarbete där vi som ombud till .SE har registrat domännamn åt er slutkunder. Detta samarbete har kontinuerligt förbättrats efter hedervärda ansträngningar från bägge parter och har under de senaste åren blommat ut i ett affärsförhållande som vi från vår sida är väldigt nöjda med.

I och med bytet som pågått mellan fredag och idag tar vi nu över kundförhållandet gentemot de domänägare som valt att avtala med oss för hantering av sina domännamn. Det är vår övertygelse att vår nya affärsrelation med .SE, den där de är vår leverantör istället för vi deras ombud, kommer vara än mer givande för båda parter.

Vi vill också visa vår uppskattning för .SEs enorma arbete under ett flertal år i samband med denna övergång. De har genomfört omfattande förändringar av hela sitt produktionssystem. De har producerat branschens bästa rutin- och policybeskrivningar. De har hjälpt till på alla sätt möjliga för att göra övergången så smärtfri som möjligt för er domänägare och för oss ombud.

Med tanke på omfattningen av detta är det en stor bedrift att allting under övergången klaffade utan minsta incident.

För detta och för allt annat hårt arbete som normalt tas för givet har vi idag skickat följande budskap till våra ärade kollegor:

Tårta som belöning för allt hårt arbete

Vi hoppas nu att ni får ta er en välförtjänt paus i åtminstone några minuter innan ni fortsätter med nästa projekt från listan. Samt att vårt samarbete under åren som följer kommer vara lika väl fungerande.

Hos oss på Loopia finns det ingen tvekan om att ni kommer att fortsätta prestera det bästa branchen har att erbjuda.

Dela: Facebooktwittergoogle_pluslinkedinmail

Kunskapsluckor

DNSSEC är visserligen det bästa som hänt Internet sedan rostat bröd, men det är också en ny teknik och som sådan så behöver man som ombud, innehavare samt allmänt Internetintresserad uppdatera sin kunskap. Det är den där konstanta vidareutbildningen som det talas om angående det moderna samhället.

En enkel miss är att man glömmer plocka bort DS-poster i förälder-zonen när man pekar om från namnservrar med DNSSEC aktivt till namnservrar som administreras av någon som ännu inte sett ljuset.

Enkel miss kanske, men det kan ta nog så länge att upptäcka om man inte har full koll på DNSSEC, vilket flytten av sennbrink.se från oss till ett mindre konkurrerande webbhotell har poängterat.

I samband med flytten från våra DNSSEC-hanterande namnservrar till det nya webbhotellets namnservrar så togs DS-posterna alltså inte bort och detta har inneburit att inga besökare med namnservrar som validerar DNSSEC kunnat besöka sidan sedan dess.

Vilka validerar då undrar ni? Vi återkommer som nämnt tidigare med bra statistik på detta, men en stor aktör är Telia och en annan är Tele2. Så i princip påverkas alla besökare från dessa två Internetleverantörer av en miss som denna.

Till Sennbrink vill jag be om ursäkt för att vi inte har kunnat hjälpa dig tidigare med denna fadäs, men jag hade lite backlog i min RSS-läsare, så jag fick Twingly-träffen på Loopia i ditt inlägg lite senare än normalt.

Dock så kan det tyckas att ditt nya webbhotell nog egentligen borde ha kunnat hjälpa dig med att få din hemsida att fungera för 46% av svenska besökare (sneak preview på statistiken, baserat på besökare till vår hemsida under december) på fem dagar.

Dela: Facebooktwittergoogle_pluslinkedinmail

DNSSEC – din kondom i Gomorra

Alla har vi väl hört talas om DNSSEC vid det här laget, vilket är för väl.

I och med att ett modernt företags domännamn kommit att bli en av dess värdefullaste resurser, känns det lite konstigt att lita på DNS ensamt med sina 16-bitars query-ID som säkerställare av att hemsidan och e-posten inte tagits över av banditer. Milt sagt. Snarare är det som att använda snustorra halmkorgar som brandskydd för viktiga dokument.
Läs mer

Dela: Facebooktwittergoogle_pluslinkedinmail

Yahoo Pipes vs Fredrik: 1-0

Eftersom Fredrik på vår marknadsavdelning inte behagade att lägga upp en RSS-feed till vår nya, fina och framförallt rättvisa hösttävling så tänkte jag vara en liten show-off med min Internet Kung Fu.

Yahoo Pipes har många av er säkert läst om (visserligen är det svårt att komma ihåg saker från så långt tillbaka i tiden, det lanserades ju för år och dagar sedan), men ni kanske inte är medvetna om riktigt hur tufft det är.

För att visa på möjligheterna med detta välputsade verktyg så har ni här ett exempel som i fyra pricksäkra Kung Fu-sparkar choppar upp Fredriks HTML och spottar ur sig prima RSS som ni sedan kan slänga in i er RSS-läsare för att slippa slita ut F5 (eller kanske snarare Kommando-R).

Länk till exemplet. Länk till RSS-flödet.

Kort genomgång

  1. Hämta vanlig slarvig HTML med Fetch page. Splitta på <div class=”pic”>.
  2. Filtera bort alla poster som inte har en bild-länk i sig, för att bara få de senaste bilderna.
  3. Kopiera resultatet till de fyra fälten som vi bryr oss om i detta exempel (title, description, pubDate och link).
  4. Gör fyra reguljära uttryck som söker fram informationen som vi vill ha ur hela bild-html-snutten och byter ut mot t.ex enbart länken.
  5. Det finns inget femte steg, vi är klara. Men sen kan man spara och publicera sin Pipe för att världen ska bli lite bättre.

Ni kan se källkoden om ni har ett Yahoo-konto och väljer Edit source från länken ovan.

Jag vet att pubDate har fel format, men jag orkade inte konvertera det. Den av er som skickar in ett exempel på hur jag konverterar från Fredriks egenhändigt valda och därför helt ostandardiserade datumformat till korrekt format för RSS vinner fem Loopia-musmattor och eternal fame i och med ett omnämnande här på bloggen.

Dela: Facebooktwittergoogle_pluslinkedinmail

Apropå Cross-Site Scripting

Den här länken är till alla er som läste vårt förra inlägg och tänkte:

Det är väl inte så farligt om någon kan få mina gästboksinlägg att bli fetmarkerade, eller en JavaScript-popup med ordet Testar att hoppa upp?

Dagens läxa: Det är viktigt att validera all indata. Någon annan kan alltid tänka ut något elakare än du. 😉

Dela: Facebooktwittergoogle_pluslinkedinmail

Världens bästa påskpyssel med jQuery

Eftersom jag inte fick några kommentarer på min utmärkta SQL-skola så tänkte jag försöka hitta något lite mer hippt och inne att skriva om så här till påskhelgen. 😉

Vad passar då bättre än en artikel om det enormt härliga JavaScript-biblioteket jQuery som på egen hand gjort att klient-baserad programmering på webben har gått från att vara timtal av frustration och nya gråa hårstrån funna under varje kaffepaus, till att frammana mentala bilder av guld, rökelse och myrra.

För att göra det hela ännu roligare så tänkte jag visa hur man på ett enkelt sätt kan använda jQuery för att göra AJAX-baserade sökningar mot en webservice som vi lagt upp så att ni har något att testa mot.

För att använda jQuery så börjar man med att lägga till följande kodsnutt i <head>-sektionen av sitt HTML-dokument:

<script type="text/javascript"
   src="/js/jquery/jquery-1.2.1.min.js">
</script>

En av de fina sakerna med jQuery är alla plugins man kan nyttja. I vårt fall kommer vi använda en plugin som heter jquery.suggest, vilken kräver att man också inkluderar jquery.dimensions.js från jQuery UI, som är ett underprojekt som tillhandahåller en rad med användbara komponenter för att bygga användargränssnitt, så vår kodsnutt för att inkludera allt godis ser ut som:

<link rel="stylesheet" type="text/css"
   href="/js/jquery/jquery.suggest.css" title="" media="screen" />
<script type="text/javascript"
   src="/js/jquery/jquery-1.2.1.min.js">
</script>
<script type="text/javascript"
   src="/js/jquery/jquery.ui-1.0/jquery.dimensions.js">
</script>
<script type="text/javascript"
   src="/js/jquery/jquery.suggest.js">
</script>

För att sedan göra om ett sökfält som har id=”domainsearch” och finns någonstans på vår sida till en AJAX-sökruta så använder vi följande kodsnutt:

$(document).ready(function() {
   $("#domainsearch").suggest("/search.php", {
      onSelect: function() {
         domain = this.value.replace(/^([^:]*):.*$/, "$1");
         status = this.value.replace(/^[^:]*:\s*(.*)$/, "$1");
         if (status == "ledig") {
            document.location.href =
               'https://www.loopia.se/bestall/bestalldoman/'
                  + domain;
         }
      }
   });
});

Detta exempel visar två koncept som är viktiga i jQuery. Först och främst så är det en bra vana att man lägger till den funktionalitet som man vill utöka sidan med på formen:

$(document).ready(function() {
   ... användbar funktionalitet här
});

När man gör på detta sätt så körs inte koden förrän sidan är helt färdigladdad, vilket oftast är önskvärt.

Det andra konceptet som visas upp är användandet av jQuery’s selector-funktionalitet för att välja vilka element som en eller flera funktioner ska appliceras på.

I detta fallet väljer vi att applicara funktionen suggest() på alla element som matchar (med CSS-syntax, se fullständig dokumentation här) #domainsearch, vilket betyder ”alla element med id satt till domainsearch”.

Funktionen suggest (vilket är vad jquery.suggest lägger till) tar en URL som ska anropas med XMLHttpRequest i bakgrunden samt ett antal parametrar, en av vilka är onSelect. onSelect anger vilken JavaScript-funktion som ska anropas när användaren har valt en rad ur sökresultatet.

Sökresultatet består av utmatningen från den anropade URL:en. Utmatningen ska ha en sökträff per rad.

Vi väljer att returnera sökresultat på formen:

doman.se:  ledig
doman.com: upptagen
doman.nu:  ledig

… och anger sedan i onSelect-funktionen att man ska skickas till vår beställningssida med domänen förvald om man väljer en ledig domän från sökresultaten.

För att mata denna lilla kodsnutt med data så använder vi oss av följande PHP-kod (sparad som search.php):

<?
   try {
      $client = new
         SoapClient('http://ws.loopia.se/LoopiaPublicServices.wsdl');

      $results = $client->search_domain_popular_tlds($_GET['q']);
         if ($results && count($results) > 0) {
            foreach ($results as $result) {
               if ($result->result == "FREE") {
                  echo $result->domain . ": ledig\n";
               } else {
                  echo $result->domain . ": upptagen\n";
               }
            }
         }
      } catch (Exception $e) {
         // Ignore exceptions, this simply means that no
         // results will be echoed to the user.
      }
?>

Ni kan ladda ned ett fullständigt exempel som bör fungera om ni bara packar upp det och laddar upp det i public_html för en domän eller subdomän hos oss.

Exemplet hittar ni också på http://sigint.se/.

Vad kan man mer göra med jQuery då? Ett vanligt användningsområde för JavaScript är att dölja och visa element när användaren gör något, t ex klickar på en knapp.

Istället för att böka med kod som:

var element = document.getElementById('someid');
if (element != null) {
	element.style.display = "none";
}

… och kämpa för att sedan få den att fungera i alla moderna webbläsare så använder man istället:

$("#someid").hide();
$("#someid").show();
$("#someid").toggle();

… med mera.

På så sätt kan man naturligtvis även använda andra selectors som t ex för class och mycket annat användbart.

Så gott som alla funktioner i jQuery, inklusive de ovan, returnerar ett nytt jQuery-object – så det går bra att kedja anropen om man vill göra flera saker med samma element. T ex:

$("#someid").fadeIn("slow").fadeOut("slow");

Vi har inkluderat lite test-animationer i exempelfilen ovan.

Hoppas att detta gett er lite kul inspiration att leka med över ledigheten.

Glad påsk!

Dela: Facebooktwittergoogle_pluslinkedinmail

Jimmys SQL-skola, del 1

Varför har psalmboken innehållsförteckning?

Många av er har säkert varit med om det. En sajt som har fungerat i flera år går plötsligt segt som sirap. Det måste vara något fel på servern. Ingenting har ju ändrats i koden.

Det första man bör tänka i fall som detta, i alla fall med en databasdriven sida (och vilken halvstor sida är inte det på den här sidan av 2000-talet?), är …

Har någon av förutsättningarna, t ex datat, kanske ändrats?

Att leta upp en psalm i ett femsidors häfte utan innehållsförteckning klarar de flesta att göra utan att försämra körens responstid, men när psalmboken växt till 631 sidor så är innehållsförteckningen bra att ha.

Motsvarigheten i databaser är korrekt indexering, vilket är lite av en konstform.

Hur väljer man då sina index för att frågorna ska kunna ställas med rimlig svarstid även med stora mängder data (för vem vill väl inte att order-tabellen ska börja snudda på 10 000-tals rader)?

Det enkla svaret är att det beror på frågorna, men det finns dock ett antal gyllene tumregler som man kan använda sig av för avgöra om en kolumn behöver ett index.

När bör index användas?

Sker det ofta sökningar på kolumnen som returnerar endast ett fåtal av alla rader i tabellen? Isåfall är ett index lämpligt. Detta är psalmboken i ett nötskal. Man frågar …

SELECT * FROM psalms WHERE title =
   'By Babel\'s streams we sat and wept'

… och endast en rad av alla psalmer matchar sökfrågan. Utan index på kolumen title så måste servern söka igenom alla rader tills den hittar psalmen, med index så tar den bara en snabb titt i indexet och hittar rätt psalm direkt.

Är kolumnen en primärnyckel eller en främmande nyckel? Man kan då anta att frågor behöver koppla ihop rader i de bägge tabellerna och därför är det vanligtvis lämpligt att ha index på dessa kolumner. Detta är mest viktigt om man ställer frågor som inte alltid returnerar alla relaterade rader (då servern i sådana fall ändå måste gå igenom alla rader).

Tänk t ex …

SELECT p.title, a.name FROM psalms p INNER JOIN author a ON a.id =
   p.author_id WHERE p.title =
   'By Babel\'s streams we sat and wept'

Utan index på primärnyckeln author.id skulle servern göra:

  • Först slår jag upp psalmtiteln i indexet för p.title.
  • Jag hittade en rad, snabbt som blixten, nu ska jag leta rätt på författaren med hjälp av mitt JOIN-uttryck.
  • Vem är nu author 42? Inte var det första raden i author-tabellen – jag kollar på andra.
  • Inte andra raden heller, jag får kolla på tredje …
  • … osv, till användaren tröttnar och går till den konkurrerande bibel-sajten.

Med index så blir skeendet ett annat:

  • Först slår jag upp psalmtiteln i indexet för p.title.
  • Jag hittade en rad, snabbt som blixten, nu ska jag leta rätt på författaren med hjälp av mitt JOIN-uttryck.
  • Vem är nu author 42? Jag kollar i innehållsförteckningen för author.id. Aha, så hette han.

Kommer vi efterfråga data sorterat efter en viss kolumn? Isåfall är ett index oftast lämpligt (ett så kallat ”clustered index” är att föredra men i MySQL, vilket vi som bekant erbjuder, så får man sådana bara för primärnycklar, eller det första unika indexet utan NULL-värden i InnoDB, med MyISAM så lagras index och data separat, så man kan inte ha data sorterat efter ett index, vilket är definitionen av ett ”clustered index”).

Kommer vi göra begränsande uttryck som består av flera kolumner, men i gengäld reducerar antalet rader i svaret avsevärt? Då är det lämpligt att ha ett kombinerat index på flera av dessa kolumner. Ta t ex:

SELECT p.title, a.name FROM psalms p INNER JOIN author a ON a.id =
   p.author_id WHERE p.title =
   'By Babel\'s streams we sat and wept' AND p.lang = 'en'

här kan ett kombinerat index på p.title och p.lang vara lämpligt:

CREATE INDEX idx_combined_title_lang ON psalms(title, lang)

Dock så får man avväga hur mycket varje extra kolumn reducerar det genomsnittliga antalet rader i svaren. Om man bara har ett språk i databasen så skulle indexet ovan inte vara bättre än det förra på enbart psalms.title. Tvärtom skulle det vara sämre, då det tar upp mer utrymme på disk och ännu värre, mer minne i index-cachen. Det ökar också arbetsmängden som behöver göras varje gång en rad läggs till eller uppdateras i tabellen. Med 300 språk i databasen så är situationen dock kanske en annan (i slutändan så måste man ändå mäta för att vara säker på vad som är lämpligast).

När ska man inte använda index då?

När man aldrig gör sökningar på en kolumn. Då gör man uppdatering och tillägg av data långsammare utan vinst.

När allt data i kolumnen är nästan identiskt. Om man gör sökningar, men sökningarna alltid returnerar ett stort antal rader från tabellen så är inte index lika lämpligt som när begränsningen blir förhållandevis stor.

Om man har en kolumn där antalet rader är högt och radernas data i kolumnen har en hög genomsnittslängd så är det ofta olämpligt att göra ett index på kolumnen då indexet tar upp för mycket minne i index-cachen. Man kan då istället göra ett index på ett några tecken långt prefix för kolumen.

Ett sätt att ta reda på hur långt prefix som är lämpligt är att helt enkelt kolla:

SELECT COUNT(*), SUBSTR(title, 1, 1) AS 'prefix'
   FROM psalms GROUP BY prefix WITH ROLLUP;
SELECT COUNT(*), SUBSTR(title, 1, 2) AS 'prefix'
   FROM psalms GROUP BY prefix WITH ROLLUP;
SELECT COUNT(*), SUBSTR(title, 1, 3) AS 'prefix'
   FROM psalms GROUP BY prefix WITH ROLLUP;

… och så vidare, sedan väljer man den lägsta längd (tredje parametern till SUBSTR) där antalet unika prefix börjar närma sig antalet rader i tabellen. I det optimala fallet så ska alltså antalet rader som frågan ovan returnerar vara värdet för COUNT(*) för ROLLUP-raden (den där prefix=NULL) + 1. Då är alla rader unika i de första N kolumnerna (där N är prefix-längden som användes i frågan). I detta fall har även alla grupper COUNT(*)=1, eftersom det bara är en rad med det prefixet.

Det här är ju jättebra, men hur analyserar jag den här komplicerade frågan?

I MySQL så är EXPLAIN din vän. I manualen så tipsas det om att man bör kolla på de rader i EXPLAIN-utmatningen där key är NULL, där type är range, index eller ALL eller där Extra innehåller ”Using filesort” eller ”Using temporary”.

Dessa rader ger en indikering på vilka tabeller i frågan som söks igenom utan index, vilket allt som oftast ger ett dåligt resultat med mycket data. Det finns även en kolumn som anger hur många rader som fråge-optimeraren uppskattar att servern ska behöva behandla för att ta fram frågeresultatet för tabellen i fråga. Höga värden där (kolumnen heter rows) är ofta synonymt med lång exekvering.

En annan variant, som vi rekommenderar starkt, är att ni skickar in frågor som ni har problem med till oss på askjimmy@loopia.se, så tar vi upp så många som möjligt av dem i nästa del av denna artikelserie. Ni får även gärna skicka in andra luriga frågor av SQL-karaktär så gör jag mitt bästa för att hitta en bra lösning åt er.

Det var allt för den här gången. Tills nästa gång så får jag önska er trevliga JOINs, trevligt aggregerande och all lycka i ert indexeringsarbete.

Dela: Facebooktwittergoogle_pluslinkedinmail