Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Mongo Replication

At a glance

...

Wiki Markup
h3. In 4.4

...

Mongo

...

 sipXconfig
In 4.4 all data required by services was replicated in IMDB (http://sourceforge.net/projects/fastdb/) and files. A change in some entities triggers a replication of relevant data. IMDB data was first written to XML files and then copied over by supervisor to specific locations. The same mechanism was in place for files. Some problem arose when dealing with a large set of users. User retrieval from the PGSQL database was not optimized and XML creation was limited by available memory and transfer via XML-RPC timed out. Prior to 4.4 the number of users supported was in the hundreds. Trying to replicate a large number of users took literally hours, in the happy case it worked at all. A lot of effort was made in 4.4 in order to optimize the SQL queries and XML-RPC transfer. 4.4 supports now thousands of users, with reasonable replication time. The replication mechanism is explained more in depth in [sipXecs:SipXecs replication mechanism].

h3. Mongo
In 4.6 the replication concept remained the same: sipXconfig gathered data from the UI, stores it in the SIPXCONFIG PostgreSQL database and writes it to a location accessible by other services. However, IMDB replication is centered around "reblicable entities". Still there are configuration files but they are handled now by *cfengine*. Those files are small and do not create OOM on their creation or XML-RPC problems on their transfer. For instance, dialing rules and service configurations are still handled this way.
However, a decision was taken to change the IMDB from *fastDB* to *MongoDB*. MongoDB has the advantage of being created to handle large amount of data (mongo comes form humongous) and having its own replication mechanism across nodes. We are also taking advantage of the Mongo's _replica set_ concept. You can read more on MongoDB on their website: http://www.mongodb.org.

...

Replicable entities

...


One main advantages of the change was that unlike versions prior to 4.6 when editing a user triggered a replication of all users (XMLs held user data in bulk), in 4.6 changing a replicable entity will trigger the insertion in Mongo of just that entity. Replication time for this kind of operation was reduced to a few milliseconds. Regenerating the whole Mongo _entity_ DB takes more time but the time is measured in minutes for even over 30.000 users on a machine with reasonable resources.

h5. Replicable entities
A *replicable* entity is an entity that will be written to *MongoDB* in its own document that can be retrieved by any service at any location. Such an entity (read Java object) will basically implement the _Replicable_ interface. For instance, _org.sipfoundry.sipxconfig.common.User_ is a replicable entity. We take for granted that each _Replicable_ is a _BeanWithId_. _Replicable_ interface has a few methods that an entity must implement.

...


{code
:java
java
}
    /**
     * Set of {@link DataSet}s to be considered for this entity
     * @return
     */
    public Set<DataSet> getDataSets();
    /**
     * Identity of the Mongo entity. It will go in the "ident" field.
     * Not all entities require it.
     * @param domainName
     * @return
     */
    public String getIdentity(String domainName);
    /**
     * Returns a collection of aliases to go in the "als" field.
     * @param domainName
     * @return
     */
    public Collection<AliasMapping> getAliasMappings(String domainName);

    /**
     * Return true if this entity is to be considered by {link ValidUsers.getValidUsers()}
     * (required by IVR)
     * @return
     */
    public boolean isValidUser();
    /**
     * Returns a Map of properties to be inserted in Mongo as is.
     * Key is the name of the field, the value is the object to be inserted.
     * @param domain
     * @return
     */
    public Map<String, Object> getMongoProperties(String domain);
{code}
A replicable entity may or may mot define a set of _DataSet_s. A _DataSet_ may be regarded as a set of common properties that are written to the Mongo document through a _DataSetGenerator_. Each _DataSet_ has its own DataSet generator that extends _AbstractDataSetGenerator_. _AbstractDataSetGenerator_ is used only to inject some common properties (like the _CoreContext_, the _SIP domain_)

...


and to define abstract methods that dataset generators need to implement.

...


Replicable entities are required to implement the methods however, not for all of them methods are relevant. For instance, not all replicables require an identity or not all replicables have aliases. Maybe this interface should be refactored.

...



h5. DataSet Generators

...


The DataSet generators are in _org.sipfoundry.sipxconfig.commserver.imdb_ package and extend _AbstractDataSetGenerator_. They are responsible for actually preparing the document that will be written to Mongo. The actual write will be done in ReplicationManagerImpl.

...

 
For instance, _org.sipfoundry.sipxconfig.commserver.imdb.Aliases_ generate method will retrieve all aliases of the entity and add them to the object in a well defined structure. _org.sipfoundry.sipxconfig.admin.commserver.imdb.Mailstore.generate(Replicable entity, DBObject top)_ retrieves all information pertinent to a user's mailstore like email address, IMAP server configuration, etc. All the information written to Mongo in a document was once stored in files. For a user we had information scattered around in different files, now the most part is kept in Mongo.

...


If you take a look at _org.sipfoundry.sipxconfig.commserver.imdb.SpeedDials_ you will see good examples of constructing the Mongo document object. You can also check out Mongo Java API (http://api.mongodb.org/java/2.6.3/).

h5.

...

 MongoConstants

...


_org.sipfoundry.commons.mongo.MongoConstants_ is the common place where field names are defined. _sipXcommons_ project is accessible by any java project.

h5.

...

 ReplicationTrigger

...


Until Mongo introduction replication triggers were scattered across different implementations. While not perfect, we tried to define a common place for all replication triggers, at least for Mongo replications. As in _sipXconfig_, save*(Object) and delete*(Object) methods are intercepted we figured this would be a good way to trigger Mongo replications. This is where _org.sipfoundry.sipxconfig.admin.commserver.imdb.ReplicationTrigger_ comes into play. It implements _DaoEventListener_ interface which defines two methods - _public void onSave(Object entity)_ and  _public void onDelete(Object entity)_ that get triggered by saving or deleting an Object. _ReplicationTrigger_ implementation of the 2 methods will mainly call the replication manager based on some conditions.

...



h5. Replication Manager

...


The replication manager  bean (_org.sipfoundry.sipxconfig.admin.commserver.imdb.ReplicationManagerImpl_) main function is to initiate the _DBCollection_, initiate the Mongo document object, delegate the construction of the Mongo document to the relevant DataSet generator and finally save the document to Mongo. It is also responsible for the parallel asynchronous replication of groups of replicable entities (groups, branches) and for the regeneration of the entire entity collection. It used to also hold the business methods to actually build the service config files that needed to be replicated and delegated the actual replication to the supervisor on the specified location. In 4.6 files are replicated using newly introduced _cfengine_ and is not object of this page.

...



_public static String getEntityId(Replicable entity)_ method defines the unique identity of the document as defined here: [http://www.mongodb.org/display/DOCS/Object+IDs]. The identity is unique and it is formed by the simple class name of the entity and the id of the object from PostgreSQL. It is by default indexed by Mongo.

...



_public DBObject findOrCreate(Replicable entity)_ method is a business method that finds an object in the Mongo entity collection or creates it if it does not exist. It also adds some properties to the object like _MongoConstants.IDENTITY_ (ident) or properties defined by each replicable entity in the _getMongoProperties_ method.

...



_public DBCollection getDbCollection()_ will instantiate the Mongo collection in which the entities are written (currently *imdb.entity*); also here we can apply some properties to the mongo connection/collection. For instance, we may define indexes:

...


{code
:java
java
}
DBObject indexes = new BasicDBObject();
           indexes.put(MongoConstants.TIMESTAMP, 1);
           entity.createIndex(indexes);

...

Replicate All - regenerate Mongo DB

...

Asynchronous Parallel Processing

...


{code}

h6. Replicate All - regenerate Mongo DB
Although great care has been taken in order to keep Postgres and Mongo in sync, some effort was put into correctly regenerating the entire configuration data if the admin feels that some of it is incorrect. The admin may access the locations page, select the desired location and trigger a "_Send profile_". Send profiles will regenerate all configurations for the selected location. If and only if the admin chooses to send profiles to the primary location, then a full regeneration of the Mongo _entity_ database will take place. _ReplicationManager_'s _public void replicateAllData()_ is the method responsible for this operation. It has 2 parts: first rewrite all the users, then rewrite all other replicable entities (OpenACD objects, conferences, authorization codes, etc). The separation was done because the most problems are put by the large number of users. We want to accommodate systems with tens of thousands of users, the other entities reach only a fraction of that number. Except users, all other entities are retrieved by providers which will search them and return them form the Postgres DB. The providers, are beans that implement _ReplicableProvider_ interface which only method is _List<Replicable> getReplicables()_. Each provider is responsible to implement the method and gather all replicable entities and to return them to the ReplicationManager which in turn will write them to Mongo using the mechanism described above.

h6. Asynchronous Parallel Processing

As mentioned before we may deal with groups containing a large set of replicable entities. Be it that we have a large group of users, or a branch containing a lot of users, or simply we want to regenerate all user data. As the reduction of the replication time was one of the goals (keeping the server functioning well being another) why not take advantage of the multi core processors that nowadays are omnipresent even in personal computers, not to mention servers. _private synchronized void doParallelAsyncReplication(int membersCount, Class<? extends ReplicationWorker> cls, Object type)_ is the method responsible with the parallel and asynchronous replication of such groups. It is syncronized because we don't want multiple threads doing heavy replication stuff at the same time (let's say we change a group with 20.000 users a few times in a row - note that changing a group will trigger a replication of all users in the group). It is also aynchronous due to the fact for heavy replication stuff, for instance when we hit "send profiles" we want the control taken to the page instantaneously and not receiving a timeout.
The admin can control the number of threads the replication to use and the number of users each thread will retrieve. All this is configurable from a properties file: *{INSTALL}/etc/sipxpbx/sipxconfig.properties*. The properties are (and the values are the default):
{code}
#mongo replication multi-threading
replicationManagerImpl.nThreads=2
replicationManagerImpl.pageSize=1000
replicationManagerImpl.useDynamicPageSize=false
{code}
Using Spring these values are injected into _ReplicationManagerImpl_.
*nThreads - number of threads to use
*pageSize - maximum number of users a thread will work with
*useDynamicPageSize - if set to true, (in this case pageSize is overwritten) each thread will work with an number of users equal to the total users divided by the number of threads.
It is the responsibility of the administrator to figure out the best values for the system. For instance if we have a quad core server dedicated to sipXecs with a few thousands of users, the default values should be fine. On the contrary if we have a dual core, then we'd want to leave the replication running on just a single thread. If we had a 8 core server with a lot of ram, allowing replication to work with 4 threads and setting _useDynamicPageSize_ to true would probably give the best results.