Agile – een manier van werken

Wij kiezen er bewust voor om agile te werken. Naast dat het uitdagend en leuk is, houdt het ons ook scherp en helpt het ons om continue te werken aan verbetering. Wij zien agile als een gedachtegoed, een verzameling van waardes die wij belangrijk vinden. We blijven continue nadenken of een bepaalde methode werkt.

De essentie van agile is dat je ervoor zorgt dat zaken relevant blijven. Concepten worden snel omgezet in resultaten. Het proces van ideevorming gaat gelijk op met software ontwikkeling. De focus komt hiermee te liggen op de resultaten.

Iteraties
Wij werken voornamelijk aan langlopende en grote projecten. Om ons te kunnen blijven focussen op resultaten delen we een project op in korte overzichtelijke periodes; ook wel iteraties genoemd. Elke iteratie is een miniatuurproject op zichzelf. Aan het einde van elke iteratie wordt er bruikbare software opgeleverd. Een groot voordeel van deze manier van werken is dat de klant direct feedback kan geven op de uitwerking van zijn ideeën.

Daarbij is bij langlopende en complexe projecten de kans groot dat gedurende het proces prioriteiten wijzigen door nieuwe inzichten en/of verschuivende doelstellingen. Doordat we nauw samenwerken met de klant, kunnen we hier eenvoudig op inspelen.

Pair programming
Naast nauwe samenwerking met de klant, werken we intern ook nauw samen. Om goedwerkende software op te kunnen leveren, vormen we bij elke stap een pair om samen te sparren én om te voorkomen dat er fouten worden gemaakt in de code. Door de goede synergie ontstaan er creatievere oplossingen en zijn we ook beter in staat om alternatieven te ontwikkelen.

Een manier van werken
Zolang Seecr bestaat werken we al agile. Voor ons is agile een prettige manier van werken omdat het veel flexibiliteit en snelle resultaten oplevert.

Wanneer je agile werkt loop je ook tegen uitdagingen aan. Daarom nemen we regelmatig deel aan agile-activiteiten om met andere agile-experts van gedachten te wisselen en elkaar te inspireren. Wil je meer weten over agile? Neem gerust contact met ons op. Wij raken over agile niet uitgepraat!

 

 

Deelname Hack-a-LOD

Afgelopen zaterdag vond in het Brabantse Veghel de Hack-a-LOD plaats. Een hackaton waar het allemaal draaide om het maken van toepassingen met Linked Open Data van onder andere de openbare bibliotheken. Deze Hack-a-LOD is onderdeel van het project Brabantse Collecties en Content. Voor meer informatie http://www.hackalod.com/.

De Nederlanse Bibliotheek Catalogus NBC+
De Nederlandse Bibliotheek Catalogus (NBC+) was hiervoor beschikbaar via de API en als ruwe data in RDF-vorm (en via sparql).  Beide technisch beschikbaar gemaakt door Seecr.  Tevens waren er verrijkingen toegevoegd door Roland Cornelissen van Metamatter.

Impressie op YouTube
Voor een korte impressie van de Hack-a-LOD, zie dit filmpje.

De catalogus en beschikbaarheid
Erik was als ‘kijker’ aanwezig en zag hoe veelal jonge mensen met de data aan de slag gingen.  De catalogus is daadwerkelijk door een team gebruikt om relevante informatie te vinden terwijl je onderweg bent.  Ze waren enigszins teleurgesteld.  Op de vraag waarom was het antwoord: “het is maar een catalogus”.  Ze bedoelden hiermee: “we vonden wel wat, maar we konden niet doorlinken, er staat alleen dat het er is”.

Het was te laat om ze te wijzen op het  NBC+ /available end-point helaas; dat had misschien in een deel van hun wensen voorzien. Het toont maar weer eens aan hoe belangrijk het is om daadwerkelijk links te geven, ook naar de inhoud zelf!

RedHat 7

Onlangs is RedHat7 gelanceerd. RedHat is één van de gesupporte Linux distributies bij Seecr. Momenteel ondersteunen we RedHat 5 en RedHat 6. RedHat 5 komt uit 2007 en begint te verouderen. Aangezien we RedHat 6 al ondersteunen gaan we per 1 januari 2015 het gebruik van RedHat 5 afbouwen. De software repository van RedHat5 blijft wel beschikbaar, maar zal niet meer actief worden bijgehouden.

Tevens gaan we beginnen met het ondersteunen van RedHat 7, om op die manier bij te blijven met de laatste ontwikkelingen van de RedHat distributie.

 

 

Digitale collectie ondersteunt nu ook SWORD data deposits

Eén van de uitbreidingen die wij de laatste maanden aan de software van de Digitale Collectie hebben toegevoegd, is de mogelijkheid om data te ontvangen via de Europeana Connection Kit (ECK).

Onder de vlag ‘Europeana Inside’ is door verschillende partijen de ECK ontwikkeld. Het doel van de ECK is om het voor erfgoedinstellingen makkelijker te maken om collecties te delen met platforms zoals Europeana en andere aggregators.

Via ECK kunnen collectiebeheerders in hun collectiebeheersysteem selecteren met welke andere diensten/aggregators de data mag worden gedeeld. ECK kan deze metadata vervolgens aanbieden als OAI-PMH repository, het tot-nu-toe gebruikelijke kanaal voor metadata aggregatie, of doorsturen in de vorm van zogenaamde SWORD data deposits.

Op 22 oktober jl. is tijdens een bijeenkomst in het Amsterdam Museum de SWORD-ingang op de Digitale Collectie gedemonstreerd.

Update HBO Kennisbank

De afgelopen weken hebben we gewerkt aan HBO Kennisbank. De release hiervan moet nog plaatsvinden, maar graag laten we alvast zien wat er is toegevoegd. Tot op heden was niet duidelijk zichtbaar welke hoge scholen bij HBO kennisbank zijn aangesloten. Daar komt nu verandering in.

HBO Kennisbank

Wat wij hebben gedaan is het filter uitgebreid. Alle deelnemende scholen worden bovenaan in alfabetische volgorde getoond met daarbij de vermelding hoeveel artikelen er beschikbaar zijn. De technische verbetering zit hem met name in het anders tonen van de organisatiefacetten.

Daarnaast hebben we ook de bestaande software bijgewerkt. We zorgen er graag voor dat onze klanten kunnen profiteren van de nieuwste functionaliteiten en prestaties die gerealiseerd zijn in andere projecten gebaseerd op dezelfde software componenten.

 

Multi-threaded expert search with Lucene

Lucene’s high-level search API parallelizes search automatically, achieving superb performance.

The low-level expert API does not do that.  Here is a solution to parallelize custom search. It is Open Source.

Introduction

The Open Source search engine Lucene has evolved into a very smart and efficient search tool.  It offers both high level search interfaces as well as low-level (expert) interfaces. If you pass it an Executor, the high-level interface (API) automatically enables all the CPU’s in a server to work on your queries as fast as possible.  The results are downright fantastic.  For quite large data sets and quite complicated queries, you will get results in milliseconds.

Concurrency and the expert interface

However, the low-level API does not put all your cores to work automatically.  That means that as soon as queries become more complicated and you have to use the low-level API, it runs slower.  That is not a problem as long as you limit the number of documents to, say, 10,000,000. Most functionality like sorting and facets still runs in a few 100 of milliseconds. But when you have many more documents and when you must support many concurrent queries, the help of more CPU’s would be very nice.

Before I introduce our solution, a quick word about the so-called expert API and the problem with it.

How the low-level API works: Collector

The main work of any search engine consists of intersecting, intersecting and yet more intersecting. Lucene’s low-level expert API gives a programmer access to the inner loop that performs the intersecting. Lucene has a hook that, regardless of what intersection algorithm it uses (zip, gallop, bisect, skip lists, and so on, look for “integer intersection algorithms”), it calls your code as soon as it determines a document is in the set defined by your query.

The hook consists of an interface called Collector, that has a method collect().  When you pass an object that supports this interface, Lucene will call collect() for each result.  This method must be very short, small and fast, since Lucene will call it many, many times.

The problem is that the search methods that accept a Collector do not enable threads and leave all but one of your CPU’s idle.  There is a good reason for that.

The problem with Collectors

The Collector interface is inherently thread-unsafe.  The reason for this is the way the setNextReader() method on Collector works. Internally, Lucene maintains an index composed of tens of segments each containing a subset of the complete index.  During search, Lucene tells the collector that a new segment starts and that the following calls to collect() are in the context of this segment.  All but the most trivial Collectors need to remember this context and this is why the Collector is unsuitable for multi-threading.  What if two threads call setNextReader() with different arguments?

Using Lucene’s internal segmenting to divide the work is quite natural. Indeed, the high-level API divides the work based on segments.  What if custom Collectors could also use this?

SuperCollector and SubCollector

We designed two new interfaces that are much like the Collector, but just different enough to support multi-threaded search for any operation.  The first is SuperCollector.  It basically replaces Collector in the call to search().  So instead of (pseudocode):

searcher.search(query, filter, collector)

we use:

searcher.search(query, filter, supercollector)

That’s all.

The SuperCollector creates subCollectors that are thread-safe:

subcollector = supercollector.subCollector(...)

The new search() method calls the method subCollector() for as many threads it wants to create. SubCollector is almost identical to Collector.  If fact, it is a subclass of Collector. It supports the setNextReader() and setScorer() methods and of course collect() and it adds only one more method:

subcollector.complete()

The thread executing the search calls complete() after it is finished. Some collectors do most of their work during collect() calls, but others just collect data for post-processing later.  Such collectors can put time-consuming post-processing in the complete() method so that it executes in parallel as well.

Encapsulating existing code

The nasty thing about the Collector API is that once you start using it, the simple parts of your query, that used to be automatically parallelized by Lucene, will now be single-threaded as well. Therefore we also need to create Super/SubCollectors for simple queries.

Simple queries are queries with results sorted on score or on a field value. The parallelization is spread across the Lucene code. We found it and encapsulated it as follows:

  1. Some code in the search method at lines 450-456 and lines 535-547 creates the threads.  We replace this code with one generic piece in search().
  2. Code for creating the TopFieldCollector at line 535 for sorting on a field while the case for sorting on score is degelated to SearcherCallableNoSort which will delegate further until a TopScoreDocCollector is finally created at line 490. We encapsulate this in specific subCollector() methods.
  3. Code for merging the results at lines 460-471 (sort on score) and at lines 550-559 (sort on field). We encapsulate this in SuperCollector.

Example: TopFieldSuperCollector

Here are the results for applying the encapsulation outlined above to the way Lucene’s default TopFieldCollector works.  These are the important steps:

First, the TopDocsSuperCollector creates the proper SubCollector when search() asks for it:

TopFieldSubCollector
createSubCollector() {
    return new TopFieldSubCollector(...);
}

The search continues as normal.  Lucene calls collect() for each result found, but this happens in a thread.  The TopFieldSubCollector simply delegates to a standard Lucene TopFieldCollector.

The search signals completion of the segment by calling complete(), which simply delegates again:

void complete() {
    this.topdocs = this.delegate.topDocs()
}

Finally, the merge happens when the application asks for the result by calling topDocs():

TopDocs topDocs(int start) {
    for (TopFieldSubCollector sub : super.subs) {
        // merge results from subcollectors
    }
    return topDocs
}

What next?

The concept above has been implemented for simple search and shows very much speed-up.  We now have to create Super- and SubCollectors for the other functions we use:

  1. for sorting by relevance: TopDocsCollector
  2. for calculating facets: FacetCollector
  3. for creating trees of collectors: MultiCollector
  4. for de-duplicating results: DeDupCollector
  5. for query-time join: KeyCollector and ScoreCollector

For each of them we are working out the design and then we will add them. I will post the results when we are done.

Meanwhile, you can follow the code here: Meresco Lucene.