blong

Scaling Search with DB_ID_RANGE

Blog Post created by blong Employee on Nov 29, 2017

Alfresco Content Services v5.2.2 introduced a new sharding method called DB_ID_RANGE.  This is the first shard scalable sharding method available to Alfresco.  Before going into the details of what I mean by shard scalable, it might be good to go through a little Alfresco Search history.

 

Before v4.0, Alfresco used Apache Lucene as its search engine.  Each Alfresco platform server contained embedded Lucene libraries.  The platform used those libraries to index all properties and contents.  In clustered environments, each server performed the same indexing operations, effectively building their own index in parallel to other instances.  Since Lucene was embedded into the Alfresco platform, it naturally scaled horizontally with the platform.

 

By default, indexing was performed synchronously or in-transaction.  This means it provided consistency to searches.  In other words, if you searched Alfresco immediately after adding or updating a node, that node would be in the result set.  The downside to this approach is that the index operation is performed in series giving the user a slower experience.  There was an option to do asynchronous indexing, however, the architecture had its own set of problems we don't need to go into for this blob post.

 

Under the embedded Lucene architecture, it was possible to scale horizontally for search performance, but only with respect to the number of search requests.  A single search must still execute completely one instance and cover the full index.  On top of that, it is not possible to scale the indexing operations.  This solution was fine for smaller repositories, but when a repository grows into the millions, it becomes untenable.

 

Along comes Alfresco v4.0 and Apache Solr was integrated with Alfresco.  Apache Solr is a web application wrapped around the Apache Lucene search engine.  So it is not a major technological shift.  The key capability here is the independent scalability of the search engine.  This was implemented using a polling model.  Basically, the Solr application polls Alfresco for new transactions since the last index, Alfresco provides the data, and Solr indexes those transactions.  Since it utilizes polling, searches become inconsistent.  This means there is a lag between updates and search results.  It also means different Solr servers could return different results, depending on their current lag.  By default, this lag is 15 seconds, so users are rarely impacted.

 

Under the Solr1 architecture, scalability was similar to Lucene.  The only difference is the ability to scale independent of the platform/backend application.  A single search still needed to execute on one Solr instance and cover the full index and each Solr instance still must index the full repository.

 

None of these scalability issues change with Solr4 or Solr6 outside of the addition of sharding or index replication.  Index replication is for a different blog post, so we will stick with sharding in this post.

 

The concept of Solr sharding was first introduced in Alfresco v5.1.  Sharding allows for an index to be divided among instances.  This means that one complete index can be distributed across 2+ servers.  This means that the indexing is scalable by a factor of the number of shards.  5 shards across 5 servers will index 5 times faster.  On top of that, a single search is also distributed in a similar matter, making a search 5 times faster.  However, a search across shards must merge search results, possibly making the search slower in the end.  When you consider loads were there are more searches than shards, you actually lose some search performance in most cases.

 

Solr shards are defined by a hash algorithm called a sharding method.  The only sharding method supported in Alfresco v5.1 was ACL_ID.  This means permissions were hashed to determine shards that contained the index.  So when a search is performed, ACLs are determined, containing shards are selected, and the search is performed, merged, and returned to the user.  It is optimal for one shard to be selected, then a small index search is performed and no result set merge is performed.  This is only beneficial if permissions are diverse.  If millions of documents have the same ACL the sharding is unbalanced and effectively useless.

 

To support other use cases, especially those without diverse sets of permissions, several sharding methods were introduced in Alfresco Content Services v5.2.  This includes DB_ID, DATE, and any custom text field.  DB_ID allows for well balanced shards in all situations.  DATE allows for well balanced shards in most situations.  That would not be the case with heavy quarter-end or year-end processing.  A well balanced shard provides the best scalability.  There good and bad reasons to choose ACL_ID or DB_ID or DATE or your own custom property.  Those are for another blog post.

 

With sharding and all these sharding methods available, most scalability issues have a solution.  However, there is still another issue.  A sharding group must have a predefined a number of shards.  This means that each shard will grow indefinitely.  So an administrator must project our the maximum repository size and create an appropriate number of shards.  This can be difficult, especially with repositories without a good retention policy.  Also, since it is best to hold the full index in memory, scalability is best when you can limit the size of each shard to something feasible given your hardware.

 

Search Engine

ProsCons

Apache Lucene

(Alfresco v3.x to v4.x)

Consistent

Scale with search requests

Embedded: no HTTP layer

No scale independence from platform

No scale with single search request

No scale with index load

Indefinite index size

Apache/Alfresco Solr v1

(Alfresco v4.0 to v5.1)

Scale independence from platform

Scale with search requests

Eventually consistent

No scale with single search request

No scale with index load

Indefinite index size

Back-end Database

(Alfresco v4.2+)

Consistent

Used alongside Solr engines

Scale with back-end database

DBA skills needed to maintain

Only available for simple queries

Apache/Alfresco Solr v4

(Alfresco v5.0+)

Same as Solr v1

Sharding available

Same as Solr v1

Alfresco Search Service v1.x

(Alfresco v5.2+)

Same as Solr v4

Embedded web container

Same as Solr v4

Shard Method: ACL_ID

(Alfresco v5.1+)

(Solr v4 or SS v1.0+)

Embedded web container

Scale independence from platform

Scale with search requests

Scale with single search request

Scale with index load

Reduction of index size

No scale for number of shards

Likely search result merge across shards

Balance depends on node permission diversity

Indefinite index size

Shard Method: DATE

(Alfresco v5.2+)

(SS v1.0+)

Same as ACL_ID

Date search performance

Reduction of index size

No scale for number of shards

Likely search result merge across shards

Index load on one shard at a time

Indefinite index size

Shard Method: custom

(Alfresco v5.2+)

(SS v1.0+)

Same as ACL_ID

Custom field search performance

Reduction of index size

No scale for number of shards

Likely search result merge across shards

Balance depends on custom field

Indefinite index size

Shard Method: DB_ID

(Alfresco v5.2+)

(SS v1.0+)

Same as ACL_ID

Always balanced

Reduction of index size

No scale for number of shards

Always search result merge across shards

Indefinite index size

Shard Method: DB_ID_RANGE

(Alfresco v5.2.2+)

(SS v1.1+)

Same as DB_ID

Scale for number of shards

Full control of index size

Proactive administration required

Always search result merge across shards

 

You can see similar comparison information in Alfresco's documentation here: Solr 6 sharding methods | Alfresco Documentation.

 

In Alfresco Content Services v5.2.2 and Alfresco Search Services v1.1.0, the sharding method DB_ID_RANGE is now available.  This allows an administrator to define a set number of nodes indexed by each shard.  This allows additional shards to be added at any time.  Although it has always been possible to add additional shards at any time (theoretically), those shards would have a new hash which would inevitably perform duplicate indexing work already performed.

 

Let's start with a fresh index.  Follow the instructions provided here: Installing and configuring Solr 6 without SSL | Alfresco Documentation.  However, ignore the initialization of the alfresco/archive core.  If you did this anyway, stop the Alfresco SS server, remove the alfresco/archive directories, and start it back up.  We basically want to start it without any indexing cores.

 

To properly enable sharding, follow the instructions here: Dynamic shard registration | Alfresco Documentation.  Although that is under Solr 4 configuration, it holds for Alfresco SS too.  I also recommend you change the solrhome/templates/rerank/conf/solrcore.properties file to meet your environment.

 

To start using DB_ID_RANGE, we are going to define the shards using simple GET requests through the browser.  In this example, we are going to start with a shard size of 100,000 nodes each.  So the 1st shard will have the 1st 100,000 nodes, the 2nd will have the next 100,000.  We will define it with just 2 shards to start.  When we need to go beyond 200,000 nodes, it would be logical to create a new shard group, starting at 200,000.  However, that does not work yet in Alfresco v5.2.  You must define a maximum number of shards that is as large as feasibly possible for your environment.

 

We are going to start with 3 server instances and grow to use 5 instances.

 

Create your 1st shard group and the 1st shard on the 1st and 2nd instances of your servers:

http://<instance1>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=1&template=rerank&shardIds=0&property.shard.method=DB_ID_RANGE&property.shard.range=0-99999
http://<instance2>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=2&template=rerank&shardIds=0&property.shard.method=DB_ID_RANGE&property.shard.range=0-99999

Create the 2nd shard on the 1st and 3rd instances of your servers:

http://<instance1>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=1&template=rerank&shardIds=1&property.shard.method=DB_ID_RANGE&property.shard.range=100000-199999
http://<instance3>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=2&template=rerank&shardIds=1&property.shard.method=DB_ID_RANGE&property.shard.range=100000-199999

For about the first 200,000 nodes added to the system, this will work for your search engine.  In this configuration, twice as much load will be placed on instance1 than the other two instances, so it is not a particularly great setup, but this is just an example for learning purposes.

 

Now let's suppose we are at 150,000 nodes and we want to get ready for the future.  Let's add some more shards.

http://<instance2>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=1&template=rerank&shardIds=2&property.shard.method=DB_ID_RANGE&property.shard.range=200000-999999
http://<instance3>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=2&template=rerank&shardIds=2&property.shard.method=DB_ID_RANGE&property.shard.range=200000-999999

Now we are ready for 800,000 more nodes and room to add 3 more shards.  Let's suppose we are now approaching 1,000,000 nodes, so let's add another 1,000,000 node chunk.

http://<instance4>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=1&template=rerank&shardIds=3&property.shard.method=DB_ID_RANGE&property.shard.range=1000000-1999999
http://<instance5>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=2&template=rerank&shardIds=3&property.shard.method=DB_ID_RANGE&property.shard.range=1000000-1999999

Suppose the search is not performing as well as you would like and you scaled vertically as much as you can.  To better distribute the search load on the new shard, you want to add another instance to the latest shard.

http://<instance1>:8983/solr/admin/cores?action=newCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7&numShards=4&numNodes=2&nodeInstance=3&template=rerank&shardIds=3&property.shard.method=DB_ID_RANGE&property.shard.range=1000000-1999999

When you move beyond 2,000,000 nodes and you want to downscale the shard above, you can try the following command to remove the shard.  Notice the coreName combines the coreName used to create the shard, appended by a dash and the shardId.

http://<instance1>:8983/solr/admin/cores?action=removeCore&storeRef=workspace://SpacesStore&coreName=alfresco-shards-0-7-3

It is recommended that you keep the commands you used to create the shards.  You should hold that in your documentation so you know which shards were defined for which DBID ranges.  The current administration console interface does not help you with that all that much.  I would expect to see that improve with future versions of Alfresco CS.

Outcomes