Polaris |
|
ActiveNaming | Grid API | Coordinator | Presentation | Polaris A | Polaris B | Download |
Polaris B implementationOverviewPolaris B is based on Polaris A. Polaris B shows how to use the Grid API to synchronize caches and replicate database updates. Cache synchronizationThe Polaris client implements a shopping basket. Users can change their shopping baskets, for instance to add new articles. Their changes stay on client side up to the time the users commit their commands. Then the Polaris Web service is invoked and records the command on server side. We want to have more than one instance of Polaris client able to serve end users, which implies that each Polaris client instance notifies its changes to the other instances. Client load balancingWe need to synchronize client instances to allow user browsers to call any client instance at any time. It is useful when a client load balancing takes place. The client load balancing is not in the PageBox scope and has to use a mechanism supported by browsers, which can be:
DNS round robinDNS round robin is supported by all browsers.
It leverages on the fact that the processing of a request includes two steps:
The DNS server returns each time a different IP address chosen in the list of the Polaris client instances. Pros:
Cons:
HTTP ProxyIt is often possible to configure a browser to use a HTTP proxy. Then the browser requests are first processed by the proxy. The mechanism was designed primarily for static content:
It is possible to implement a proxy that balances dynamic requests between Application server instances. Pros:
Cons:
Server-driven load balancingEnd users access the Application server pages through a Welcome page. The Welcome page can include links to the Application server pages or redirect to an Application server page. The Welcome page is a dynamic page that sets the link or redirection URL from the list of running Application instances. This solution can look stupid at first: we use a dynamic page to balance requests between dynamic page servers and to enhance their reliability. However the reliability of a server page depends on the application complexity. A dynamic page that doesn’t implement a business logic, that doesn’t access a database nor write on a log file, can be as reliable and almost as fast as a static page. We name this Welcome page routing page. Here is a simple implementation.
A routing server hosts a routing page. The routing page selects an URL in a table of Active Application server instances. This in-memory table is populated by a Web service, we call here I’m_here. When a server page of an Application server instance is invoked it calls I’m_here each time a timeout has expired. When an URL of the active instance table has been selected but has expired (is older than timeout), the routing page removes this URL from the active instance table. Pros:
CommentsMultiple server solutions tend to broaden the gap between the downtime perceived by the end users (typically in the range 25-33%) and the downtime measured by the operators (typically in the range 0.2-0.5%). Nobody lies:
We have seen that load balancing implies sending and processing extra messages to detect downtime conditions. The Application server instances must also manage the same data, which means that each instance must notify its changes to the other instances. It is what we call cache synchronization. It is obvious that
We can note that at best the number of load balancing messages grows linearly with the number of Application server instances. The number of cache synchronization messages can be constant with multicast or broadcast but the processing cost still grows linearly because each instance processes all cache messages. Therefore there is a scalability limit. ProtocolAt a given time an authenticated user handles a shopping basket that contains a set of articles in given quantities. A shopping basket is represented by a Hastable containing items whose key is the article name and the value is a CmdRow object. The CmdRow class contains the number of articles and repeats for convenience the article name and price. Cache structureTherefore we can represent the cache data with:
We need two protocols:
Request processingThe Polaris application sends and receives cache data during the processing of user requests:
Sequence diagramA user can be served by any Application server instance. Every user has a different user account, which means that at a given time only one Application server instance processes this user requests and acts as the synchronization source.
The first request of user 1 is processed by the first Application server instance. The user adds an article to its shopping basket. The server page first retrieves messages from other Application server instances to update the cache. Even if it is the first time user 1 updates its shopping basket, this action allows updating the cache entries of other users. Then the server page adds the article to the cache and uses the GridAPI to send the update to the other Application server instances. The second request of user 1 is processed by the second Application server instance. The user adds another article. The server page first retrieves messages from other Application server instances to update the cache. Then the server page adds the article to the cache and uses the GridAPI to send the update to the other Application server instances. The third request of user 1 is processed by the first Application server instance. The user commits its command. The server page first retrieves messages from other Application server instances to update the cache. Then the server page calls the Polaris Web service with the user’s shopping basket as parameter. Next the server page removes the user entry from the cache and uses the GridAPI to send the update to the other Application server instances. Synchronization protocolThe synchronization protocol uses the Grid API in UDP transport mode with if possible multicast. The server page only sends the change data. The change can be:
Seqno is a sequence number incremented at each message by the source instance. If a message is skipped then the destination instance uses the newcomer protocol to refresh its cache. Newcomer protocolThe newcomer protocol uses a SOAP Web service implemented by Polaris B and called Newcomer. This Web service implements a GetCache method that returns an XML string:
Where c stands for cache, u for user, a for article, n for name, q for quantity, d for destination and s for sequence number. The XML string contains a cache image and a destination array. This destination array contains the last sequence numbers used to build this cache image and the URL of the corresponding instance of the Newcomer Web service. The newcomer starts receiving synchronization messages before getting the response to its Newcomer call and uses the destination array to discard messages older than the cache image. The newcomer uses the GetParms method of the Grid API to retrieve the URLs of the Newcomer web service of Application instances already involved in the cache synchronization. Then it randomly selects one of the newcomer Web service URLs, instantiates a proxy and calls its GetCache method. Note: The newcomer processes requests only once its cache is populated. ScopeThe ActiveNaming Web service uses the concept of location to implement location-dependent routing. A Web service instance is chosen if it has the same location as the client. We need a similar concept to define the set of Application instances whose caches are synchronized. For simplicity Polaris reuses the ActiveNaming location. Database replicationActiveNamingThe Polaris Web application (the client) uses the ActiveNaming to perform
When the user of a PolClient instance in Location 1 commits a shopping basket for a user belonging to range 1, the ActiveNaming balances PolServer invocation between instances defined in Location 1 and managing Range 1 data. We must replicate data updates on all instances managing the same user range even they are not in the same location and even if they don’t run at the time the data update occurs. We accept that the replication takes some time: if we update PolServer i and then query PolServer j PolServer j has not necessarily yet processed the change occurred on PolServer i. ProtocolDataPolServer implements a ShoppingOrder Web service. When a PolClient instance commits a shopping basket it invokes an Order method of this ShoppingOrder service. Order has a prototype:
The OrderRow class describes an order row:
The PolServer instance that processed the Order request builds an object containing the user ID (accountID) and an array of OrderRow (lines) and calls the Grid API in SMTP transport mode. The Grid API sends a single mail to the mail addresses of the other PolServer instances managing the same user range. ScopeThe user range defines the set of Polserver instances whose updates are replicated to each other. The user range is made of {userLow } : {userHigh} where userLow is the lowest user number in the range and userHigh is the highest user number in the range. Request handlingPolServer instances subscribe a message handler to the Grid API. This handler is called each time the Grid API receives a mail. The handler method runs in the Listener thread and simulates a database update (PolServer doesn’t actually access a database). Synchronization implementationThe cache synchronization is implemented in PolClient. ShoppingBasket.ascx.csThe cache synchronization is mostly implemented in the ShoppingBasket class defined in ShoppingBasket.ascx.cs. ShoppingBasket is the code behind the ShoppingBasket user control. The ShoppingBasket Page_Load, OnAdd, OnDelete, Prepare and Commit methods are modified and new - private - methods are added, Initialize and SetCache are added. Read the Polaris A implementation document for a presentation of ShoppingBasket. The behavior of ShoppingBasket changes on a single point: users must be authenticated before using the ShoppingBasket control. This is needed because the account ID is the only way to link the Shopping basket of an Application instance to the Shopping baskets of the other Application instances. The main change in the code is that ShoppingBasket uses the cache and no longer keep shopping basket data in the Coordinator heap. Grid parametersThese parameters are additional parameters of the ShoppingBasket control.
InitializeInitialize is called by Page_Load. The function of Initialize is to populate and update the cache and the sequence numbers of the other applications participating to the cache synchronization. The cache is stored in a static Hashtable called cache. The key of the cache items is the user name and the value of the cache items is itself a Hashtable whose items have article name as keys and article number as values. The sequence numbers are stored in a static Hashtable called seqnos, whose items have the Newcomer URL of the destination as key and the destination sequence number as value. Because cache and seqnos are static the cache is application-server wide. Initialize first instantiates the Grid object. It uses two service methods, ComputePageBoxUrl and ComputeNewcomerURL to compute the URL of the PolClient controlling PageBox and Newcomer Web service, using the URL of the ShoppingBasket control. When cache is not initialized, Initialize calls SetCache to populate it. Then Initialize reads messages from the other applications participating to the cache synchronization and updates the cache accordingly. Initialize handles the synchronization protocol described above. Synchronization messages are sent in Synch messages. The Synch class has this definition:
Initialize ignores Synch messages with a sequence number lower than the corresponding entry in seqnos. Initialize applies the change described by the other Synch messages to cache. SetCacheSetCache uses the Newcomer protocol to create cache and seqnos. SetCache first calls the GetParms method of the Grid object to retrieve the URLs of the Newcomer Web services of the other application instances. Then it selects randomly one of this URL. Next SetCache instantiates a Newcomer proxy with this URL and calls its GetCache method to get the cache in the Newcomer XML format described above. Eventually SetCache parses this XML stream to populate cache and seqnos. Page_LoadPage_Load is called when the ShoppingBasket control is loaded. Page_Load calls Initialize to create or refresh the cache and the seqnos Hashtables. At the first invocation of a ShoppingBasket instance (when IsPostBack is false), Page_Load retrieve the user Shopping Basket from the cache and uses a BuildCmdRows method to convert the cache data to an array of CmdRow. Then Page_Load binds the article Datagrid to this array. Note: When IsPostBack is true, Page_Load cannot use the cache data. Other instance updates will be reflected in the OnAdd and OnDelete methods. OnAddOnAdd is called when the user clicks on the Add button. OnAdd first check that the user is authenticated. If the user is not authenticated OnAdd displays a message "Authenticate first". Then OnAdd retrieves the shopping basket from the cache, updates the shopping basket and creates a Synch with the user request, uses the Grid API to scatter the Synch on the other Application instances. Next OnAdd updates the cache and calls BuildCmdRows to convert the cache data to an array of CmdRow. Eventually OnAdd binds the article Datagrid to this array. OnDeleteOnDelete is called when the user clicks on a Delete button of the article Datagrid. OnDelete first check that the user is authenticated. If the user is not authenticated OnDelete displays a message "Authenticate first". Then OnDelete retrieves the shopping basket from the cache, updates the shopping basket and creates a Synch with the user request, uses the Grid API to scatter the Synch on the other Application instances. Next OnDelete updates the cache and calls BuildCmdRows to convert the cache data to an array of CmdRow. Eventually OnDelete binds the article Datagrid to this array. PreparePrepare is a method of the Coordinator’s Transaction interface. Prepare is called when a source calls the Commit method. Prepare is modified in two ways in Polaris B:
CommitCommit is a method of the Coordinator’s Transaction interface. Commit is called when a source calls the Commit method. Commit is modified in Polaris B to check in the cache if there is a shopping basket to commit and to remove the user’s shopping basket from the cache, to populate a Synch object and to use the Grid API to remove the shopping basket from the other Application instances. RollbackRollback is a method of the Coordinator’s Transaction interface. Rollback is called when a source calls the Rollback method. Rollback is modified in Polaris B to check in the cache if there is a shopping basket to rollback and to remove the user’s shopping basket from the cache, to populate a Synch object and to use the Grid API to remove the shopping basket from the other Application instances. Newcomer.asmx.csThe Newcomer Web service is a service implemented by PolClient and allowing other PolClient instances to get an image of the cache. The Newcomer Web service implements a single method, GetCache:
GetCache returns a byte array containing a cache image in the format described in the Newcomer protocol section. GetCache first creates an XmlTextWriter on a MemoryStream. Then GetCache enumerates the cache and seqnos content and writes the corresponding elements on the XmlTextWriter. Eventually GetCache returns MemoryStream.GetBuffer(). NewcomerProxy.csNewcomerProxy.cs is a proxy of the Newcomer Web service. NewcomerProxy.cs has been generated using wsdl and modified to allow setting the Web service URL in the constructor. The NewcomerProxy class is called by ShoppingBasket.SetCache. Replication implementationThe replication is implemented in PolServer. ShoppingOrderThe ShoppingOrder class is defined in the ShoppingOrder.asmx.cs file. The support of the replication is mainly implemented in three static methods:
StartGridStartGrid calls the ReadConfig methods to read the Grid parameters. Then StartGrid instantiates a Grid object and calls its Subscribe method with a Callback object as parameter. StartGrid has two formats:
Where:
The second format instantiates a Grid object to start processing and sending replication messages. In addition to that the first format insert a PolServer instance in a list of replicated instances. ReadConfigReadConfig has the same two formats as StartGrid:
PolServer uses the data-dependent routing range (userLow and userHigh) to define at Grid instantiation which list of replicated instances this Polserver instance belongs to. When ReadConfig is called with the first format it saves the data-dependent routing range in a file called PolRange.txt in the PolServer directory. When ReadConfig is called with the second format it reads the data-dependent routing range from PolRange.txt. ReadConfig2ReadConfig expects to find a configuration file called PolServer.xml in the PolServer directory that contains:
Where:
ReadConfig uses an XmlTextReader to parse the PolServer.xml file. ReadConfig also sets the log file used by the Grid API to gridLog.txt and defines a different log file for messages of PolServer called ShoppingOrder.txt. Both files are created in the Polserver directory. The main function of ShoppingOrder.txt is to log update requests. OrderThe Order Web service method is modified in Polaris B to implement replication. If the Grid class is not yet instantiated, Order creates it Note: This code was also added to the CheckAccount method. Replication messages are wrapped in a Command class:
The Command class contains the same data as the Order method, the user name (accountID) and the article list (lines). Then the Order method:
CallbackThe Callback class implements the GridCallback interface and therefore a Notify method. The Callback class is defined in the ShoppingOrder.asmx.cs file. NotifyBecause StartGrid subscribes a Callback object the Grid instance calls the Notify method each time it receives a message from another PolServer instance. The Notify method casts the message object into a Command object and logs its content in ShoppingOrder.txt. RangeLocThe RangeLoc class is defined in the RangeLoc.aspx.cs file. The only change in Polaris B is in the OnSet method. OnSetOnSet is called when the user clicks on the Set button. In the Polaris B implementation, OnSet calls the StartGrid method of ShoppingOrder.
Contact:support@pagebox.net |