Difference between revisions of "Apertium scalable service"

From Apertium
Jump to navigation Jump to search
(Redirected page to Apertium-apy)
 
(28 intermediate revisions by 6 users not shown)
Line 1: Line 1:
#redirect[[apertium-apy]]
=Introduction=

==Project Information==

This is one of the 9 Google Summer of Code projects accepted for Apertium (see http://socghop.appspot.com/org/home/google/gsoc2009/apertium).

'''Student:''' Víctor Manuel Sánchez Cartagena, University of Alicante, Spain

'''Mentor:''' Juan Antonio Pérez-Ortiz, from Transducens Group, University of Alicante, Spain.

==Introduction==

Currently Apertium is a very useful translation platform, and hopefully it will be even more useful in the future, when new language pairs will be added.

But, if another application wants to profit from Apertium power, Apertium needs to be installed on the same machine. Although installing Apertium is not a very difficult task, as linguistic data are frequently updated, the installation should be updated often too. Moreover, communication between an external application and Apertium is not easy to code, because Apertium only reads input text from standard input.

There is another option: to use the simple web service currently located at http://www.apertium.org/, but it has two major problems:

* Its features are quite limited, since it cannot list available language pairs, and only accepts http GET and POST parameters.
* As it starts a new Apertium instance for each request, it consumes a lot of computer resources, making scalability difficult, especially when there is only a single server.

So, the aim of this project is to build an application wrapper for Apertium with a public web service API (both REST and SOAP) that allows third-party programmers to access it from their desktop or web applications, and request the same operations that can be done with a local installation. The key feature of this application is scalability. It is intended to balance high loads by scheduling and prioritizing pending translations according to the server-side resources available. Environments which will be considered can be static, where there is a fixed amount of servers available, or dynamic, as in elastic cloud computing services. When working in dynamic mode, new servers will be automatically added when load rises. The availability of highly scalable web services for Apertium will catalyze the worldwide use and adoption of the platform in lots of translation contexts.

==Technical challenges and application features==

One of the first challenges to overcome is the design of a easy-to-use API. A difficult API would stop many developers from integrating Apertium into their applications. Therefore, it would be a great idea to study other popular translation APIs. We plan to use both REST and SOAP technologies to give developers as many options as possible. However, the REST web service will not be be totally RESTful, since it will accept translation requests over HTTP POST in order to overcome HTTP GET length limits.

Currently Apertium's scalability is strongly limited by the fact that it cannot run as a daemon, and has to be launched from scratch every time a translation is required. In a web service environment, continuously launching and terminating Apertium processes by the operative system would cause a very strong overhead. In fact, in preliminary experiments I found that, in a common desktop system, processing more than 10 simultaneous translation requests of around 10000 words each makes the system get out of resources when launching an Apertium instance for each request. However, if we spread the requests between a pair of daemons using a queue, the system keeps responding and it takes less time to perform all the translations.

I have made a very simple implementation of an Apertium daemon for testing purposes. It's a quite simple program that launches Apertium and opens a pipe attached to its standard input. Since the pipe is never closed, the Apertium process never dies. Different translation requests are surrounded in the input stream by special XML tags. However, this is not very useful because, sometimes, Apertium does not output short translations until it receives a new request. This happens because information is stored in buffers, and they are only flushed when they are full or the pipeline processes finish (this never happens in daemon mode). Overcoming this problem probably involves changing Apertium core.

But the most difficult challenge is designing a highly scalable and reliable system that distributes the translation requests between Apertium daemons hosted in different servers (probably there will be more than one daemon per server), and starts or shutdowns daemons on demand. A daemon works only with a pair of languages, because changing the languages would imply instantiating pipeline processes with different dictionaries. In addition, in elastic cloud computing environments (where servers are requested and released on demand) we also need to know when to stop using a server or allocating a new one. So, it is necessary to use load balancing features like priority activation, priority queuing, etc.

There are a lot of fast open source load balancing systems, but most of them are highly web application oriented. So, they only implement simple load balancing algorithms, based on the amount of traffic already assigned to each server, server response time, etc. However, we need to take into account a different source of information in order to forward the request to the right server, as the language pairs of the daemons available in each one. And, since most of the time of a request is spent in the Apertium daemon, it will be better to implement a new load balancing system able to deal with our specific requirements.

The Java platform has a good built-in support for priority queues (see http://java.sun.com/javase/6/docs/api/java/util/AbstractQueue.html and its subclasses) and communication between servers (with the RMI protocol), so using it would be a good option. Additionally, there are completely open source Java implementations and the Apache Axis2 web services engine supports both SOAP and REST web services.

Security must be a very important feature of the system. Applications should register to grant reliable access to the API, and connections from unregistered clients will be limited to a fixed amount per IP.

==Working plan==

Community bonding period: Study queue and load balancing algorithms, and their possible implementations on Java platform. Study RMI, different ways of daemonizing Apertium and Axis2 web services engine.

* Week 1: Implement daemon mode
* Week 2: "
* Week 3: Test Apertium daemon. Check if it is fault-tolerant and as fast as expected.
* Week 4: Define API and implement some methods without load balancing nor on-demand daemon management.

Deliverable #1: Some of the API methods allow to translate with Apertium using a fixed number of daemons and a single computer.

* Week 5: Implement a protocol for communication between servers
* Week 6: Design and implement load balancing and daemon management algorithm.
* Week 7: "
* Week 8: Implement all API methods

Deliverable #2: API fully implemented, dynamic daemon management with fixed number of servers.

* Week 9: Implement dynamic server management for an elastic cloud hosting environment. Amazon EC2 is probably the best option, but Eucalyptus could also be an alternative, as it is open source and its interface is compatible with Amazon EC2.
* Week 10: "
* Week 11: Testing, evaluation and full documentation. Ensure that the API is well-documented and any external developer can easily integrate Apertium into his/her application.
* Week 12: "
* Week 13: Extra time for schedule slips.

Project completed. Final deliverable: Highly scalable web service application working in both dynamic and static environments, with customizable load balancing.

==Student skills and experience==

Last September, I finished my degree in Computer Engineering at University of Alicante, and now I am studying a postgraduate diploma in Application Development with Java Enterprise Technology. Next November I will start a Doctorate Programme in Computing Applications.

I have some experience in open source projects:

* ANTLArbol is a tool that builds parse trees from an execution of a parser/translator written in Java with the ANTLR tool. It was my degree dissertation and now is used by Compiler Design students in University of Alicante to debug their compilers and translators. More information: http://code.google.com/p/antlrarbol/

* Currently I am working for the Transducens group in University of Alicante, Spain. I am developing an open-source web project related to social translation around Apertium. We plan to release an early prototype in the next weeks. As a result of this work, I have learnt a lot about Apertium design and its limitations, and I have detected the need for having a highly scalable web service around Apertium.

==See also==

* [[Apertium going SOA]]


=Development status=

==1st week==

Apertium now works as a daemon. The same instance can process many translation requests, as they are separated by a superblank with a special comment.
Null flush option (-z) is implemented in all modules except deformatters and reformatters. So, the daemon outputs the translation as soon as it is available, but deformatter and reformatter are invoked one time each per translation. The overhead of invoking them is quite small and this way we can use the same daemon to translate different format inputs.

Project is split into 2 subprojects:

* ApertiumServerWS is the request router. It processes Web Service requests and sends them via RMI to the right ApertiumServerGSOC instance. It is also the placement controller, telling each ApertiumServerGSOC the language pairs it should work with. If we detect that this module acts as a bottleneck, we can run more than one instance and share the placement algorithm object via Terracotta (http://www.terracotta.org/).

* ApertiumServerGSOC is a set of Apertium daemons running in the same machine. It processes translation requests sent via RMI. The request router also asks each ApertiumServerGSOC for a list of running daemons, and tells them to start or stop some daemons.

At the time of writing ApertiumServerWS can only work with one ApertiumServerGSOC instance, and only allocates one daemon, for the pair es-ca.

==2nd week==

* Updated API to match Google AJAX Language API (only translation API, not language detection). Implemented batch processing interface too. More information: http://code.google.com/intl/es/apis/ajaxlanguage/documentation/reference.html#_intro_fonje . Added an API method to list language pairs.

* Simple web interface to test JSON parsing.

* Updated communication protocol. Now when an instance of ApertiumServerGSOC starts, it registers with the request router. The request router asks it for supported pairs and updates its list of servers. When the router receives a translation request, sends it to a server that has a daemon for the requested language pair (parameters like server load are not taken into account). If there isn't any server with a suitable daemon, the router asks one server to create a daemon. The protocol is not very useful, since it doesn't work well on high load situations (it is necessary to allocate more daemons for the same pair) or when there are requests of many different language pairs.

* Test with JMeter. 500 sequential translation requests, i.e. a request is sent when the response to the previous one is received. Source text in Spanish, 1884 characters. Translate it to Catalan. Test file is available in SVN, inside ApertiumServerWS project.

Processing each request takes an average of 102ms

[[Image:vApertiumServerTestGraphDaemon.jpg]]


If we change ApertiumServerGSOC code , to make it invoke the whole Apertium pipeline for each request, the average time is 901 ms

[[Image:vApertiumServerTestGraphNoDaemon.jpg]]


Of course, this is a special case where all requests are directed to the same daemon. If there were more different languages in the requests than the maximum number of running daemons, daemons would be stopped and started many times, so the difference between some approaches wouldn't be so big. However, having more than one daemon for the same language pair could make the first test even faster.

==3rd week==

* Tested application with long inputs (about 2 MiB of text).
* Now it is possible to know CPU and memory consumption of each daemon.
* Implemented a proof of concept of null flush in deformatters and reformatters, but this feature hasn't been tested enough yet

==4rd week==

* Tested null flush in deformatters and reformatters. This feature will be disabled. The fact that flex stores input in memory buffers makes having a reliable implementation very difficult. I'll deal with it in the future if I have enough time.
* Implemented null flush in Constraint Grammar. There have been some difficulties because it reads Unicode input with ICU library, and I/O functions from this library always report EOF when they read a '\0'. Patch submitted and accepted by VISLCG3 project.
* Now all the stable pairs can work as a daemon.

==1st deliverable==

* JSON API allows translating and listing language pairs.
* All stable pairs are available.
* A daemon is created for each pair (this behavior will change in the future).
* You can launch more than a server, but load balancing algorithm will only take into account the first one.

==5th week==

* Fixed bug in apertium-interchunk related to null flush. Committed patch.
* Done some load tests, found interesting conclusions:
**A single daemon consumes all the CPU capacity of a server, at least in computers with 2 o less CPUs.
**The average translation time of requests processed by the same daemon (if it's the only running daemon in the computer) is smaller than the average time when requests are spread between some daemons in the same computer.
**There's no reason to run more than one daemon for the same language pair in the same machine. Admission control procedures (not implemented yet) will allow changing daemon priorities.
* Studied some papers about application placement:
** [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.81.1564&rep=rep1&type=pdf A Scalable Application Placement Controller for Enterprise Data Centers]
** [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.80.7874&rep=rep1&type=pdf Dynamic Placement for clustered web applications]
** [http://personals.ac.upc.edu/dcarrera/papers/NOMS2008.pdf Utility-based Placement of Dynamic Web Applications with Fairness Goals]
** [http://www.cs.ncl.ac.uk/publications/trs/papers/1126.pdf Concurrent Management of Composite Services According to Response Time SLAs]

==6th week==
* Starting to implement placement system described by [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.81.1564&rep=rep1&type=pdf A Scalable Application Placement Controller for Enterprise Data Centers].
** Implemented [http://en.wikipedia.org/wiki/Ford-Fulkerson_algorithm Ford-Fulkerson algorithm] to solve [http://en.wikipedia.org/wiki/Maximum_flow_problem max flow] problem.
** Implemented [http://en.wikipedia.org/wiki/Bellman-Ford_algorithm Bellman-Ford algorithm]. A combination of Ford-Fulkerson and Bellman-Ford algorithms can solve [http://en.wikipedia.org/wiki/Minimum_cost_flow_problem#Relation_to_other_problems minimum cost maximum flow] problem.

==7th week==
*Implemented core of placement system: Placement Controller. Given a set of machines and applications, and which applications are deployed on which machine, the Placement Controller gives a new placement solution that maximizes the amount of satisfied application demand and minimizes the amount of application starts and stops.

==8th week==
*Implemented the whole system: placement executor, queue scheduler, admission control, etc.
*Started testing

==2nd deliverable==
*Dynamic daemon management with a fixed number of servers. It is not tested enough and have some important bugs
*JSON API implemented. SOAP and XML-RPC APIs not implemented yet

==9th week==
* Debug dynamic daemon management.

==10th week==
* Debug dynamic daemon management and implement more sophisticated version of queue scheduler.

==Steps to test the application==
In the machine where the request router will run, checkout ApertiumServerRMIInterfaces and ApertiumServerRouter from my branch:
<pre>
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerRMIInterfaces
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerRouter
</pre>
Then install ApertiumServerRMIInterfaces in the local Maven repository:
<pre>
cd ApertiumServerRMIInterfaces
mvn install
</pre>
Edit ApertiumServerRouter/src/main/resources/configuration.properties and set the property requestrouter_rmi_host to the name of the host where it will run.Now we are ready to compile ApertiumServerRouter, with:
<pre>
cd ../ApertiumServerRouter
mvn package
</pre>
Start rmimregistry, use port 1098, with the command:
<pre>
rmiregistry 1098
</pre>
Finally the ApertiumServerRouter war file from "target" directory in your favorite web server.



In the machine(s) where the apertium instances will run, checkout ApertiumServerRMIInterfaces and ApertiumServerSlave from my branch:
<pre>
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerRMIInterfaces
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerSlave
</pre>
Then install ApertiumServerRMIInterfaces in the local Maven repository:
<pre>
cd ApertiumServerRMIInterfaces
mvn install
</pre>
Now we are ready to compile ApertiumServerSlave, with:
<pre>
cd ../ApertiumServerSlave
mvn package
</pre>
Unzip target/ApertiumServerSlave-1.0-assembled.zip in your preferred installation directory.
Install Apertium with the script installApertiumAndPairs.sh
Edit conf/configuration.properties and set the property requestrouter_host to the name of the host where ApertiumServerRouter will run.
Finally, run the server with the script run-apertium-server.sh. Use as the first parameter the name of the host where ApertiumServerSlave is running.


Now browse index.jsp page of ApertiumServerRouter web application. If something went wrong, you can check the logs at /tmp.


=Documentation=

==API Specification==

===Introduction===

This API is very similar to Google AJAX Language API to make as easy as possible switching to Apertium JSON API.
For more information about Google AJAX Language API, see http://code.google.com/intl/en/apis/ajaxlanguage/documentation/reference.html#_intro_fonje .

There are two resources:
<pre>
http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate
</pre>
and
<pre>
http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/listPairs
</pre>

The first one translates pieces of plain text or html code, and the second one lists the available language pairs.

Both resources admit GET and POST http methods. The value of arguments must be properly escaped (e.g., via the functional equivalent of Javascript's encodeURIComponent() method).

===Common arguments and response format===

These arguments are all optional and common to both resources:
* '''key''' : User's personal API key. Requests from registered users have higher priority.
* '''callback''' : Alters the response format, adding a call to a Javascript function. See description below.
* '''context''' : If callback parameter is supplied too, adds additional arguments to the function call. See description below.

If nor callback neither context arguments are supplied, this is the JSON object returned by both resources:
<pre>
{ "responseData" : JSON Object with the requested data , "responseStatus" : Response numeric code , "responseDetails" : Error description }
</pre>

If callback argument is supplied, a call to a function named by the callback value is returned. For instance, if callback argument's value is ''foo'',
this is the JavaScript code returned:
<pre>
foo({ "responseData" : JSON Object with the requested data , "responseStatus" : Response numeric code , "responseDetails" : Error description })
</pre>

If both callback and context arguments are supplied, the returned function call has more arguments. If callback's value is ''foo'' and context's value
is '''bar'':
<pre>
foo('bar',JSON Object with the requested data , Response numeric code , Error description )
</pre>

===listPairs resource===

This resource only accepts the common arguments.

The response data returned is an array of language pairs, following this format:
<pre>
[{"sourceLanguage": source language code ,"targetLanguage": target language code }, ... ]
</pre>

responseStatus is always 200, that means the request was processed OK, and responseDetails has a null value.

So if we call this resource with no arguments:
<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/listPairs'
</pre>
we get, for example:
<pre>
{"responseData":[{"sourceLanguage":"ca","targetLanguage":"oc"},{"sourceLanguage":"en","targetLanguage":"es"}],"responseDetails":null,"responseStatus":200}
</pre>

===translate resource===

This resource accepts the common arguments mentioned above, plus the following specific arguments:
* '''q''' : Source text or HTML code to be translated. Compulsory argument.
* '''langpair''' : Source language code and target language code, separated by '|' character, which is escaped as '%7C'. Compulsory argument.
* '''format''' : Source format. ''text'' for plain text and ''html'' for HTML code. This argument is optional. If this argument is missing it is assumed that source is plain text.

The response data is JSON object following this format:
<pre>
{ "translatedText" : translated text }
</pre>

Many different response status codes can be returned. This is the list with all the codes and their meaning:
* '''200''' : Text has been translated successfully, ''responseDetails'' field is null.
* '''400''' : Bad parameters. A compulsory argument is missing, or there is an argument with wrong format. A more accurate description can be found in ''responseDetails'' field.
* '''451''' : Not supported pair. Apertium can't translate with the requested language pair.
* '''452''' : Not supported format. The translation engine doesn't recognize the requested format.
* '''500''' : Unexpected error. An unexpected error happened. Depending on the error, a more accurate description can be found in ''responseDetails'' field.
* '''552''' : Overloaded system. The system is overloaded and can't process the request.

Here is a simple example. Requesting a translation with:
<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate?q=hello%20world&langpair=en%7Ces&callback=foo'
</pre>
the result is:
<pre>
foo({"responseData":{"translatedText":"hola Mundo"},"responseDetails":null,"responseStatus":200})
</pre>
And if we add the context parameter:
<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate?q=hello%20world&langpair=en%7Ces&callback=foo&context=a'
</pre>
we get
<pre>
foo('a',{"translatedText":"hola Mundo"},200,null)
</pre>

===Batch interface===

More than one translation can be performed in the same request if we use more than one ''q'' argument or more than one ''langpair''. If there is only one
''q'' argument and more than one ''langpair'' arguments, the same input string is translated with different language pairs. If there is only one ''langpair'' argument and more than one ''q'' arguments, the different input strings are translated with the same language pair. And if both arguments are supplied more than one time, and they are repeated exactly the same times, the first ''q'' is translated with the first ''langpair'', the second ''q'' with the second ''langpair'', etc.

The returned JSON changes a bit when using the batch interface. Now the field ''responseData'' contains an array of JSON objects, each one with the usual fields: ''responseData'', ''responseStatus'' and ''responseDetails''. Note that we have particular values of ''responseStatus'' and ''responseDetails'' for each translation, but global values too. If all the translation are OK, these values match, but if there is an error in any translation, global values of these fields take the value of the erroneous translation. If there is more than one erroneous translation, global fields take the value of one the the erroneus translations.

These examples show the described behaviour:
<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate?q=hello%20world&q=bye&langpair=en%7Ces'
</pre>
<pre>
{"responseData":[{"responseData":{"translatedText":"hola Mundo"},"responseDetails":null,"responseStatus":200},
{"responseData":{"translatedText":"adiós"},"responseDetails":null,"responseStatus":200}],"responseDetails":null,"responseStatus":200}
</pre>


<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate?q=hello%20world&langpair=en%7Ces&langpair=en%7Cca&callback=foo'
</pre>
<pre>
foo({"responseData":[{"responseData":{"translatedText":"hola Mundo"},"responseDetails":null,"responseStatus":200},
{"responseData":{"translatedText":"Món d'hola"},"responseDetails":null,"responseStatus":200}],"responseDetails":null,"responseStatus":200})
</pre>


<pre>
curl 'http://ApertiumServerInstallationHost/ApertiumServerRouter/resources/translate?q=hello%20world&q=goodbye&langpair=en%7Ces&langpair=en%7Cca&callback=foo&context=bar'
</pre>
<pre>
foo('bar',[{"responseData":{"translatedText":"hola Mundo"},"responseDetails":null,"responseStatus":200},
{"responseData":{"translatedText":"adéu"},"responseDetails":null,"responseStatus":200}],200,null)
</pre>

==User manual==

===System architecture===

There are two main applications that make the web service work:

* '''ApertiumServerRouter''': Runs on a JavaEE web container (like [http://tomcat.apache.org/ Apache Tomcat]) and processes the HTTP translation requests. Spreads them between the different translation servers (that have Apertium installed). It also manages the different Apertium daemons running on the translation servers and, under certain circumstances, can start and stop translation servers.

* '''ApertiumServerSlave''' : It's a simple Java application that runs on the translation servers. These servers must have Apertium installed. Receives translation requests from ''ApertiumServerRouter'' and sends them to the running Apertium instances. Note that the system is designed to run many ''ApertiumServerSlave'' instances (one per server) and only one ''ApertiumServerRouter'' instance.

===Getting it===

At the moment, the only way to get the applications is downloading its source code and compiling them. You'll need to download the source code of three projects from the Apertium svn repository. Before executing the following commands, be sure you have [http://subversion.tigris.org/ Subversion] installed.
<pre>
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerRMIInterfaces
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerSlave
svn co http://apertium.svn.sourceforge.net/svnroot/apertium/branches/gsoc2009/vitaka/ApertiumServerRouter
</pre>

To compile the source code you'll need:
* A Java Development Kit compatible with Java version 6. It can be Sun's implementation or any other implementation that follows the specification (see [http://en.wikipedia.org/wiki/Java_Development_Kit#Other_JDKs]).
* Maven. If you don't have Maven installed, simply [http://maven.apache.org/download.html download] it, unzip it, and be sure that the ''bin'' directory is in your PATH.

Once you are sure you have Java JDK and Maven, you can build the applications.
* Build ''ApertiumServerRMIInterfaces''. This project contains the common classes of ''ApertiumServerSlave'' and ''ApertiumServerRouter'':
<pre>
cd ApertiumServerRMIInterfaces
mvn install
</pre>
* Build ''ApertiumServerSlave'':
<pre>
cd ApertiumServerSlave
mvn package
</pre>
The compiled project can be found in target/ApertiumServerSlave-1.0-assembled.zip
* Build ''ApertiumServerRouter''
<pre>
cd ApertiumServerRouter
mvn package
</pre>
The compiled project can be found in target/ApertiumServerRouter.war

* If you need the javadoc of any of the projects, from its root directory execute:
<pre>
mvn javadoc:javadoc
</pre>
And the javadoc website will be generated in target/site/apidocs

===Installing===

====ApertiumServerSlave====

Unzip ''ApertiumServerSlave-1.0-assembled.zip'' to the directory where you want to install it. Be sure that the machine has Internet connection, because the installation script will download Apertium from its SVN repository.

Then run the script ''installApertiumAndPairs.sh'' with:
<pre>
./installApertiumAndPairs.sh
</pre>

or

<pre>
bash installApertiumAndPairs.sh
</pre>

By default it will download and install Apertium and all the stable pairs, and install them under /home/youruser/local. You can change these this options with the following parameters:
* '''-p''' Installation_prefix : Changes the installation prefix. If you run the script with the options ''-p /foo/bar'' it will install executables under /foo/bar/bin, libraries under /foo/bar/lib, etc.
* '''-l''' pair1,pair2,pair3... : Installs only the specified language pairs. The list of pairs must be a subset of the list of stable pairs that can be found in [http://wiki.apertium.org/wiki/Main_Page Apertium wiki main page]. Note that the language order must be the same that the one in main page, although translators in both ways will be installed, e.g. ''-p en-es'' will install translators from Spanish to English and from English to Spanish, but ''-p es-en'' won't install any translator. There are pairs that only install a translator in one way, see the arrows in Main page.

When installation is complete, you can safely remove ''apertium'' directory. ''ApertiumServerSlave'' can't work with an existing Apertium installation, because it modifies Apertium modes files to make it run as a daemon.

====ApertiumServerRouter====

As this application is packaged as a ready-to-deploy war file, there is no need to installation. To run it simply follow the instructions of your Java web container. But before running it, you'll probably need to configure it.

===Configuring===

====ApertiumServerSlave====

Application options can be changed by editing ''INSTALLATION_DIRECTORY/conf/configuration.properties''. These are the options that can be changed and their meaning:
* '''requestrouter_host''': Name of the host where ''ApertiumServerRouter'' is running. When this application starts, it contacts ''ApertiumServerRouter'' to tell that the server is ready to perform translations. '''This is the only property you'll need to change to make the system work'''.
* '''requestrouter_port''': Port of ''requestrouter_host'' on which rmiregistry is listening. Default value is 1098.
* '''requestrouter_objectname''': Name of the RMI object exported by ''ApertiumSeverRouter''. If you don't modify it in''ApertiumSeverRouter'' 's configuration, the default value is OK.
* '''memoryrate_64bit''': It is known that programs generally need more memory in 64-bit operative systems than on 32-bit ones. If the application is running on a 64-bit operative system, its free memory is multiplied by the value of this property. The default value is 0.6087. It is not recommended to change it. See the calibration section to know how to change this value.
* '''daemon_frozen_time''': If an Apertium instance doesn't emit any output during this time (in milliseconds), having received an input, we assume it is frozen. The default value, 20 seconds should be OK. Change it only if the system reports false frozen daemons.
*'''daemon_check_status_period''': Daemon status checking period, in milliseconds. A very low period can cause system overload, so there is no need to change this value.
* '''apertium_timeout''': Maximum time, in milliseconds, Apertium can take to perform a translation. If this time is exceeded, an error is returned to ''ApertiumServerRouter''. Its default value is very high, so timeouts are only reported when there are unexpected errors.
* '''apertium_max_deformat''': Maximum number of simultaneously running Apertium deformatters. To tranlate a text, first it is deformatted launching an instance of the corresponding apertium deformatter (text deformatter or html deformatter), then it is sent to the right daemon, and finally, the daemon result is reformatted launching an instance of the corresponding apertium reformatter. The system's bottleneck is in the daemons, so the default value for this property is 1.
* '''apertium_max_reformat''': Maximum number of simultaneously running Apertium reformatters. The default value is 1.
* '''apertium_null_mode_suffix''': Suffix that all the modes that allow Apertium running as a daemon share. Don't change it.
* '''apertium_supported_pairs''': Comma-separated list of language pairs the system can translate with (because they can work as daemons). In this case the first code is the source language and the second code, the target language. So, we'll have both ''en-es'' and ''es-en''. Don't modify this property. Its value is set by the installation script described above.
* '''apertium_path''': Prefix of the directories where Apertium is installed. Don't modify this property. Its value is set by the installation script described above. If you change this value to pint to an existing Apertium installation, it won't work, because the Apertium installatin needs to be made with the provided installation script, that creates new modes files.


====ApertiumServerRouter====

Editing ''ApertiumServerRouter'' properties is a bit more difficult. You'll need to unzip ''ApertiumServerRouter.war'', change the desired configuration properties and zip its content again. Main configuration options are located in file ''WEB-INF/classes/configuration.properties''. These
are the options present in this file:
* '''requestrouter_rmi_host''': Name of the host where ''ApertiumServerRouter'' will run. '''This is the only property you'll need to change to make the system work'''.
* '''rmi_registry_port''': Port on which ''rmiregistry'' is listening. Default value is 1098, so you'll need to manually start ''rmiregistry'' on port 1098. Remember that ''rmiregistry'' must run on the machine where ''ApertiumServerRouter'' runs, as well as on machines running ''ApertiumServerSlave''. The difference is that ''ApertiumServerSlave'' starts RMIRegistry automatically, but ''ApertiumServerRouter'' doesn't, because of the restrictions of running in a Java web container.
* '''requestrouter_rmi_name''': Name of the RMI remote object exported by ''ApertiumServerRouter''. The default value is OK if you don't modify the ''requestrouter_objectname'' property of ''ApertiumServerSlave''.
* '''requestrouter_rmi_port''': Port on which RMI remote object exported by ''ApertiumServerRouter'' will listen. There is no need to modify it, unless you get an exception saying "port not available".
* '''admissioncontrol_interval''': Period, in milliseconds, of Admission control updating. Admission control is the subsystem that decides whether a request should be accepted or not, depending on system's load. Don't change this value unless you really know what you are doing.
* '''admissioncontrol_treshold''': If system "calculated load" is over this threshold, requests won't be accepted. The default value has been tested and should work OK, but if requests are rejected while the system is not overloaded, try to increase this value.
* '''admissioncontrol_k''': We get "calculated load" by combining real load and "calculated load" in the previous instant: calculated_load = real_load*k+previous_load*(1-k). The default value have been tested and it is not recommended to change it.
* '''placement_controller_execution_period''': Period, in milliseconds, of Placement controller execution. Placement controller decides which language pairs run on each translation server. This is a critic value. Changing it could make the system crash, so it is better to leave the default value.
* '''server_status_updater_execution_period''': Period, in milliseconds, of server status checking. It is recommended to leave the default value.
* '''scheduler_maxcharacters_in_daemon_queue''': If the number of characters of a language pair being translated by a server is lower than this value. a translation request of that language pair is sent to the server.It is recommended to leave the default value.
* '''scheduler_maxelements_in_daemon_queue''': If the number of request of a language pair being translated by a server is lower than this value. a translation request of that language pair is sent to the server.It is recommended to leave the default value.
* '''scheduler_not_registered_priority_increment''': The higher, the less priority unregistered users have.
* '''scheduler_timeout''': Maximum time, in milliseconds, a server can take to perform a translation. If this time is exceeded, an error is returned. Its default value is very high, so timeouts are only reported when there are unexpected errors.
* '''load_prediction_alpha''': It is very similar to admission control k. The predicted load of the different language pairs is calculated by combining the amount (and size) of requests received during a period of time, and the predicted load before this period, so predicted_load = measured_load*alpha+previous_prediceted_load*(1-alpha). Default value has been tested and it is not recommended to change it.
* '''request_k''': Constant CPU cost of processing a request. The CPU cost of a translation request is calculated by adding this value to the number of characters of the request. Don't change it.

To keep track of registered users and give them higher priority, their data are stored in a MySQL database. Database connection properties are configured in [http://java.sun.com/javaee/technologies/persistence.jsp?intcmp=3282 JPA] configuration file:''WEB-INF/classes/META-INF/persistence.xml''. By default, it connects to a database called '''ApertiumWSUsers''' on localhost, with username '''apertium''' and password '''apertium'''.

===Running===
Firstly, run ''ApertiumServerRouter'' by deploying your re-zipped ''ApertiumServerRouter.war'' in your Java web server. For example, in Apache Tomcat, put that file in the directory called ''webapps''.

Then, run ''ApertiumServerSlave'' on each of the servers you want to use to perform translations. Use the script ''run-apertium-server.sh'' and add a parameter with the name of the host where ''ApertiumServerSlave'' runs:
<pre>
bash run-apertium-server.sh hostname
</pre>
It will calculate the server's capacity by performing a series of translations and store it in ''conf/capacity.properties''. If you have already run ''ApertiumServerSlave'' previously and you don't want to wait for the capacity calculation, add the argument ''-capacityFromConfigFile''. Using this argument capacity is read from ''conf/capacity.properties'' and the startup time decreases.
<pre>
bash run-apertium-server.sh hostname -capacityFromConfigFile
</pre>
After reading or calculating capacity, it contacts ''ApertiumServerRouter'' and starts to receive translation requests.
You can tune RMI ports and remote object name by editing ''run-apertium-server.sh''. See javadoc of class ''com.gsoc.apertium.translationengines.main.Main'' for more information.

Of course, servers can be stopped (with Ctrl+C) or started at any time.

===Dynamic server management: local networks===
If you don't want to manually start and stop translation servers, ''ApertiumServerRouter'' can do it for you. It will decide to start or stop servers depending on the translation capacity needed by the incoming requests. You'll only have change some configuration properties, and ''ApertiumServerRouter'' will connect via SSH to the computers of your network where ''ApertiumServerSlave'' is installed, and run or stop it when needed. This is called '''On Demand Server Management''' mode.

To make ''ApertiumServerRouter'' work in On Demand Server Management mode, you'll have to follow a couple of additional configuration steps. After unzipping ''ApertiumServerRouter.war'' and editing WEB-INF/classes/configuration.properties, and before zipping it again, edit the following files located at ''WEB-INF/classes/'':
* '''OnDemandServerInterface.properties''': Contains general options about dynamic server management:
** '''class''': Class that contacts servers to start and stop ''ApertiumServerSlave'' instances. Use the default value: ''com.gsoc.apertium.translationengines.router.ondemandservers.LocalNetworkOnDemandServer''.
** '''maxServers''': Maximum number of servers started by ''ApertiumServerRouter''. Must be equal or lower than the number of elements in the list of servers.
** ''maxInactivityTime'': Maximum time, in milliseconds, a server can run without receiving any load. After this time, the server is stopped.
** ''startUpTimeout'': Maximum time, in milliseconds, the system waits for newly started server to contact the request router. The default value should be fine.
* '''LocalNetworkOnDemandServer.properties''': Contains options about how to contact new servers when running in On Demand Server Management mode using class ''com.gsoc.apertium.translationengines.router.ondemandservers.LocalNetworkOnDemandServer''.
** ''hosts'': Comma-separated list of servers with ''ApertiumServerSlave'' installed. Each element of the list follows this format: ''username:password@hostname:path''. ''Username'' and ''password'' must belong to an existing user on the remote machine. ''Hostname'' is the host name of the remote machine, and ''path'', the path where ''ApertiumServerSlave'' is installed. ''username'', ''password'' and ''path'' are optional. If they are not specified, their values are read from the properties ''defaultUser'', ''defaultPasword'' and ''defaultPath'' respectively.
** ''defaultUser'': Default user name.
** ''defaultPassword'': Default password.
** ''defaultPath'': Default ''ApertiumServerSlave'' installation path.

===Dynamic server management: Amazon EC2===
If you plan to deploy this Apertium web service implementation with dynamic server management on Amazon EC2, it is recommended to change the configuration explained in the previous section. With this new configuration, new server instances will be started and stopped, instead of starting and stopping the application on existing servers.

After unzipping ''ApertiumServerRouter.war'' and editing WEB-INF/classes/configuration.properties, and before zipping it again, edit the following files located at ''WEB-INF/classes/'':

* '''OnDemandServerInterface.properties''': Contains general options about dynamic server management:
** '''class''': Class that contacts servers to start and stop ''ApertiumServerSlave'' instances. Use: ''com.gsoc.apertium.translationengines.router.ondemandservers.AmazonOnDemandServer''.
** '''maxServers''': Maximum number of EC2 server instances started by ''ApertiumServerRouter''.
** ''maxInactivityTime'': Maximum time, in milliseconds, a server can run without receiving any load. After this time, the server is stopped.
** ''startUpTimeout'': Maximum time, in milliseconds, the system waits for newly started server to contact the request router. The default value should be fine.

* '''AmazonOnDemandServer.properties''': Contains options about how to start new servers when running in On Demand Server Management mode using class ''com.gsoc.apertium.translationengines.router.ondemandservers.AmazonOnDemandServer''.
**'''amazon_id''': Your Amazon Web Service Access Key ID. Compulsory property.
**'''amazon_key''': Your Amazon Web Service Secret Access Key. Compulsory property.
**'''amazon_image_id''': ID of an AMI that must run ''ApertiumServerSlave'' when started. Compulsory property.
**''amazon_security_groups'': comma-separated list of security groups associated with the server instances that will be started. These groups must allow connections to the following ports:
***Port on which RMI Registry runs: 1099.
***Port on which the ''ApertiumServerSlave'' RMI remote object is exported: 1331.
**''amazon_key_name'': Key pair associated with the server instances that will be started. Necessary if you want to manually connect via SSH to the instances and check that everything works as expected.
**''amazon_region_url'': URL of the region where the new instances will be launched and the AMI will be looked for. If this property is not present, region EU-West is used.
**''amazon_avzone'': availability zone where the new instances will be launched. It is a good idea to launch the request router and the apertium instances in the same availability zone. If you include the scripts explained below in your AMIs, you won't need to edit this property.

====Building AMIs for Amazon EC2====

=====Bootstrapping=====

To avoid creating new AMIs when a new version of Apertium Web Service is released, it is recommended to use a mechanism called bootstrapping. When the AMI starts, it downloads a package from Amazon S3, unzips it, and executes the script inside the package. The package also contains the lastest version of ''ApertiumServerSlave'' or ''ApertiumServerRouter'', so the script installs it and changes the necessary configuration properties.

=====ApertiumServerRouter AMI=====

We followed these steps to create an AMI that runs ''ApertiumServerRouter'':
* Start a clean installation of Ubuntu 9.04 Base.
* Install JRE 6.
* Install MySQL. Create the database and user specified in JPA configuration file. Give the user the right permissions.
* Install s3cmd:<pre>apt-get install s3cmd</pre> As root user, configure it with your Amazon WS ID and secret key:<pre>s3cmd --configure</pre>
* Install unzip:<pre>apt-get install unzip</pre>
* Prepare bootstrap:
** Put the file ''bootstrap'' that can be found in ''ApertiumServerRouter source code root/misc/ec2'' in ''/etc/init.d'', and a symbolic link from ''/etc/rc2.d/S99bootstrap'' to ''/etc/init.d/bootstrap'': <pre>ln -s /etc/init.d/bootstrap etc/rc2.d/S99bootstrap</pre>
** Put a file called ''bootstrap.tar.gz'' in a S3 bucket called ''org.apertium.server.router.bootstrap''. This file must contain a version of ApertiumServerRouter.war configured to run on Amazon EC2 and the script ''bootstrap.sh'' that can be found in ''ApertiumServerRouter source code root/misc/ec2''.
* Create AMI using EC2 commands.

=====ApertiumServerSlave AMI=====
We followed these steps to create an AMI that runs ''ApertiumServerRouter'':
* Start a clean installation of Ubuntu 9.04 Base.
* Install JRE 6.
* Install s3cmd:<pre>apt-get install s3cmd</pre> As root user, configure it with your Amazon WS ID and secret key:<pre>s3cmd --configure</pre>
* Install libraries needed to run Apertium:<pre>sudo apt-get install subversion build-essential g++ pkg-config libxml2 libxml2-dev libxml2-utils xsltproc flex automake autoconf libtool libpcre3-dev </pre>
* Install ICU library:<pre>apt-get install libicu-dev</pre>
* Install Apertium. To do so, compile ''ApertiumServerSlave'' and copy ''ApertiumServerSlave-1.0-assembled.zip'' to the running AMI. Unzip it and run ''installApertiumAndPairs.sh''. Remember the values of the properties ''apertium_path'' and ''apertium_supported_pairs'' from ''conf/configuration.properties'', after installing Apertium.
* Run ''run-apertium-server.sh''. Wait for server capacity to be calculated and then kill it. Keep the file ''capacity.properties'' that have been created in ''conf'' directory.
* Prepare bootstrap:
** Put the file ''bootstrap'' that can be found in ''ApertiumServerSlave source code root/misc/ec2'' in ''/etc/init.d'', and a symbolic link from ''/etc/rc2.d/S99bootstrap'' to ''/etc/init.d/bootstrap'': <pre>ln -s /etc/init.d/bootstrap etc/rc2.d/S99bootstrap</pre>
** Put a file called ''bootstrap.tar.gz'' in a S3 bucket called ''org.apertium.server.slave.bootstrap''. This file must contain ''ApertiumServerSlave-1.0-assembled.zip'', the script ''bootstrap.sh'' that can be found in ''ApertiumServerSlave source code root/misc/ec2'' and the file ''capacity.properties'' created in the previous step. Note that this file should only be used on an EC2 with the same size than the instance where the file was created. But before packing ''bootstrap.tar.gz'', edit ''bootstrap.sh'' and write the values of the properties mentioned above at the beginning of the script.
* You can remove the directory ''ApertiumServerSlave-1.0'' to decrease the size of the AMI.
* Create AMI using EC2 commands.

===Advanced configuration: calibration===

There are some advanced configuration properties we didn't explain in previous sections. This Apertium Web Service implementation estimates the CPU and memory capacity of each server, and the amount of load (for each language pair) the system will have to process (based on previous requests). Then, starts and stops daemons in the different servers to meet the load requirements. The CPU capacity is measured as the number of characters of a Spanish plain text that the server can translate into Catalan during a second.

====Load Converter====

The amount of load predicted for each language pair is based on the number of requests received for that pair during a past period of time and the number of characters of each request.
As the CPU capacity needed to translate a fixed amount of characters depends on the language pair, it is necessary to convert the amount of characters of each request to the equivalent Spanish-Catalan amount of characters
, i.e. , the amount of characters that needs the same CPU capacity to be translated from Spanish to Catalan than the original amount of characters to translated with the original language pair.
Something similar happens with the format. The CPU capacity needed to translate a fixed amount of characters depends on the format. Usually the same amount of characters needs less CPU capacity when it is in HTML format, because HTML tags are not translated. So, the amount of characters of each HTML request is converted to the equivalent, i.e. that needs the same CPU capacity to be translated, amount of plain text characters.
Applying these two conversions to the number of characters of a request, it can be compared with server's capacity.
To convert load between language pairs and formats, the conversion rates are stored in ''LoadConverter.properties'' are used. This file is located in the root of ''ApertiumServerRouter'''s classpath. There are different types of properties in this file:
* ''source_language_code''-''target_language_code'': Contains the rate to convert from an amount of characters of the pair named by this property key to the equivalent Spanish-Catalan amount of characters.
* ''format_html'': Rate to convert from an amount of characters with HTML format to the equivalent plain text amount of characters.

These values have been already calculated but in the future they won't be very accurate because the rules of the different language pairs will change and so will their speed. If you want to calculate them again, execute this command from an existing installation of ''ApertiumServerSlave'': <pre>java -jar ApertiumServerSlave-1.0.jar -pairsInformation -comparationPair es-ca -speedFile LoadConverter.properties -memoryFile MemoryRequirements.properties</pre>
A new version of ''LoadConverter.properties'' will appear in ''ApertiumServerSlave'' installation directory.

====Memory requirements====

To place each daemon on the right server the system needs to know how much memory is needed by each language pair. This information is stored in ''MemoryRequirements.properties''. This file is located in the root of ''ApertiumServerRouter'''s classpath. For each property, the key is a language pair and the value the amount of megabytes of memory it requires.
These values have been already calculated but in the future they won't be very accurate because the rules of the different language pairs will change and so will their memory requirements. If you want to calculate them again, execute this command from an existing installation of ''ApertiumServerSlave'': <pre>java -jar ApertiumServerSlave-1.0.jar -pairsInformation -comparationPair es-ca -speedFile LoadConverter.properties -memoryFile MemoryRequirements.properties</pre>
A new version of ''MemoryRequirements.properties'' will appear in ''ApertiumServerSlave'' installation directory.

[[Category:Development]]

Latest revision as of 08:39, 7 March 2018

Redirect to: