Merge branch 'master' into passingSide

This commit is contained in:
Axel Uhl
2013-02-27 13:08:24 -10:00
9 changed files with 227 additions and 10 deletions
+10 -10
View File
@@ -1,25 +1,25 @@
# Welcome to the SAP Sailing Wiki
This is the <img src="http://analysis.sapsailing.com/themes/logo.png" height="58" width="200" /> Wiki where useful information regarding this project can be found. This Wiki is being stored directly into the default GIT repository on branch **master**. You can edit information here freely but do not leave any nonsense here.
This is the <img src="http://analysis.sapsailing.com/themes/logo.png" height="58" width="200" /> Wiki where useful information regarding this project can be found.
The syntax is relatively easy and is documented here http://daringfireball.net/projects/markdown/dingus. When you create a new Page then make sure to put some thinking into the right tree structure and then just extend the URL above.
### The Pitch
**ATTENTION**:
Like businesses, sailors need the latest information to make strategic decisions - but they need it even faster. One wrong tack, a false estimation of the current, or the slightest wind shift can cost the skipper the entire race. As premium sponsor of the Kieler Woche 2011, and co-sponsor of Sailing Team Germany (STG), SAP is showing how innovative IT solutions providing real time data analysis can give teams the competitive edge.
* Make sure to always have _wiki/_ as the base path. So if you want to create `server/SystemBoot` then the correct URL is `wiki/server/SystemBoot`. Make sure to add you Page to the Table of Contents below by editing the document.
* Password can be found in the encrypted page [[Passwords|wiki/passwords]]. You can decrypt it by using `gpg -d < passwords.md` after having entered the password. If you want to edit, first decrypt and then encrypt again using `gpg -a --symmetric passwords.md`. Make sure to never save the unencrypted version!
If you have any questions then either contact Axel Uhl, Jens Rommel, Frank Mittag or Simon Pamiés.
### Table of Contents Wiki
### Table of Contents
* [[How to work with this wiki|wiki/howto]]
* [[General Project Information|wiki/general-information]]
* [[OnBoarding Information|wiki/onboarding]]
* [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]]
* [[Server Replication|wiki/server-replication]]
* [[Configure Races on Server|wiki/configure-races-on-server]]
* [[Planning|wiki/planning]]
### Internal services (not related to wiki but useful)
* [Bugzilla Issue Tracking System](http://bugzilla.sapsailing.com/bugzilla/)
* [GIT Repository (SAP)](ssh://git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git)
* [Maven Repository Browser](http://maven.sapsailing.com/maven/)
* [Main Sailing Website](http://www.sapsailing.com)
* [Visitor Statistics](http://analysis.sapsailing.com/)
@@ -26,4 +26,18 @@
version="1.1.0.v20090501071000"
unpack="false"/>
<plugin
id="org.mockito.mockito-core"
download-size="0"
install-size="0"
version="1.9.5"
unpack="false"/>
<plugin
id="org.objenesis"
download-size="0"
install-size="0"
version="1.3"
unpack="false"/>
</feature>
+22
View File
@@ -0,0 +1,22 @@
# General Project Information
### The Situation
Sailing races can be difficult to understand and follow for an audience as they happen live, out on the water. The trouble starts with figuring out who's in the lead and doesn't stop with understanding the impact of the current race's ranks on the overall regatta standings which are subject to non-trivial scoring rules.
### Project Description
Using GPS trackers and wind sensors, it is possible to collect real-world data about a sailing race and analyze and visualize the race live. These visualizations can be used by a commentator while talking to an audience, directly by the audience over online channels on various devices, as well as by the sailors who can assess their performance, also relative to their competitors, in replay mode.
<img src="/wiki/images/ScreenShotRunningApp.jpg"/>
### Objective
We're doing this in the scope of SAP's sport sponsorship efforts, also in order to show technology leadership and to combine an exciting technology showcase with our sponsorship.
### Approach
The project started shortly before Kiel Week 2011, in May, leaving little time for planning and developing a first prototype to show in Kiel. The SAP Center of Excellence (CoE) supported (and still supports) the project by skilled development resources and good connections to flexible external hosting providers assisting in the operations of the application. We started from scratch with a Java application using the Equinox OSGi implementation as its runtime infrastructure. The first bundles to be developed were the sailing domain abstractions (boats, boat classes, sailors, courses, races, events, wind and the geometry model) and the connectors for GPS and wind data connectivity. The web front end was initially developed using Python, connecting to the Java backend through HTTP. We chose a NoSQL database (MongoDB) to store data not otherwise retrievable from the tracking provider later, such as the wind data and administrative information. JUnit tests for the back end, a central Maven build with Hudson/Jenkins integration, a GIT repository for change management and the use of Bugzilla laid the technical ground for our development environment. Given our microscopic team size that varied between one and three developers, we skipped daily scrums and used on-demand ad-hoc chats, also through IM systems, as a replacement. Our deadlines for a release are rigid: the sailing regattas at which we present the solution.
After successfully demonstrating the prototype at Kiel Week, the project entered a second phase with major architecture clean-ups. We removed the Python server and replaced it by the use of the Google Web Toolkit (GWT). We were particularly impressed by the client-side performance and flexibility of its powerful controls such as tables and list displays. Web designers, though, need some time to find their way into the world of GWT. We ported the leaderboard functionality from Python to GWT and successfully presented it in its new shape at the International German Championship 2011 in Travemuende and at the Championship of the Champions later that year in Hamburg.
What followed were improvements in the analytical treatment of GPS data and the integration of a flexible Google Map-based race visualization. We added algorithms to identify and count the various sailing maneuvers for the competitors (tacks, jibes, penalty circles, etc.) and implemented a wind estimation algorithm that analyzes the boats' courses and by means of clustering them by tack and leg type infers the most probable wind direction together with a confidence value. A first pragmatic approach to replicating the so far UDP-provided wind data through an HTTP push channel to additional server instances running on spectator boats and serving the solution to mobile devices on these boats rounded off the release presented at an SAP-internal sailing event. When this release with only minor extensions was presented at a training regatta in S'Arenal, Mallorca, our server got under heavy load because the URL had virally spread, and sailors showed high interest in the solution. We saved the event with some ad-hoc performance patches. The event taught us the limitations of dynamically re-computing various key figures each time a tiny bit of raw data changed and brought us to think more about smarter cache invalidation strategies. Additionally, we now started to work on server instance replication techniques that will help us leverage the cloud to scale our solution out during the peak loads experienced during live events.
### The Java In-Memory Challenge
More or less co-incidentally we chose an architecture in which we keep all relevant race data in main memory. We applied a few techniques to avoid excessive amounts of object headers to consume too much heap space while preserving the API "look & feel" of individual, fine-grained objects for things like speed, bearing, time points, distances or positions. With this, all tracking data of Kiel Week and several other events easily fit into the 16GB of heap space we allocate for our production instance. Key technical challenges remain the performance of live calculations of complex key figures with quickly changing tracking data because it makes caching and cache invalidation tough topics scaling out the server with its in-memory state while allowing client sessions to run modifying transactions on each server instance achieving great performance with a Google Maps integration when the number of boats to display and the expectations regarding the quality of the boat displays rise.
+13
View File
@@ -0,0 +1,13 @@
# How to work with this Wiki
This Wiki is being stored directly into the default GIT repository on branch **master**. You can edit information here freely but do not leave any nonsense here.
The syntax of the markup is relatively easy and is documented here http://daringfireball.net/projects/markdown/dingus. When you create a new Page then make sure to put some thinking into the right tree structure and then just extend the URL above.
**ATTENTION**:
* Make sure to always have _wiki/_ as the base path. So if you want to create `server/SystemBoot` then the correct URL is `wiki/server/SystemBoot`. Make sure to add you Page to the Table of Contents below by editing the document.
* Password can be found in the encrypted page [[Passwords|wiki/passwords]]. You can decrypt it by using `gpg -d < passwords.md` after having entered the password. If you want to edit, first decrypt and then encrypt again using `gpg -a --symmetric passwords.md`. Make sure to never save the unencrypted version!
If you have any questions then either contact Axel Uhl, Jens Rommel, Frank Mittag or Simon Pamiés.
Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

+75
View File
@@ -0,0 +1,75 @@
# OnBoarding Information
This document describes the onboarding process for a new team member (developer)
### Race Analysis Development Setup
#### Installations
1. Eclipse (e.g. Eclipse Classic 3.7.2), http://www.eclipse.org
2. Eclipse Extensions
* Install Eclipse GWT should be version 2.4
3. Git (e.g. msysGit for Windows v1.7.10), http://git-scm.com
4. MongoDB (e.g. Production Release 2.0.4), download: http://www.mongodb.org/
5. RabbitMQ, download from http://www.rabbitmq.com/. Requires Erlang to be installed. RabbitMQ installer will assist in installing Erlang.
6. JDK 1.6 (Java SE 6), http://jdk6.java.net (for GWT)
7. JDK 1.7 (Java SE 7), http://jdk7.java.net
8. Maven 3, http://maven.apache.org
#### Further optional but recommended installations
1. Cygwin, http://www.cygwin.com/
2. Eclipse Mylyn Bugzilla extension
3. kdiff3 (git tool)
4. Firebug (javascript & .css debugging)
#### Accounts
1. Git Account
- Register yourself as a Git user in the SAP-Git under: https://git.wdf.sap.corp:8080/
- Ask the Git administrator (Axel Uhl) to get on the list of enabled committers
2. Bugzilla
- Ask the Bugzilla administrator (Frank Mittag, Axel Uhl) to create a bugzilla account for you.
- Bugzilla url: http://sapcoe-app01.pironet-ndh.com/bugzilla/
3. Race Analysis user
- Add yourself as an user to the Race Analysis suite by adding a Jetty user in the file
java\target\configuration\jetty\etc\realm.properties
#### Steps to build and run the Race Analysis Suite
1. Get the content of the git repository
- Clone the repository to your local file system from ssh://[SAP-User]@git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git or ssh://[user]@sapsailing.com/home/trac/git
2. Check out the 'master' branch from the git repository. The 'master' branch is the main development branch. Please check that you start your work on this branch.
3. Setup and configure Eclipse
- Make absolutely sure to import CodeFormatter.xml (from $GIT_HOME/java) into your Eclipse preferences (Preferences->Java->Code Style->Fortmatter)
- Install the Eclipse GWT-Plugin (now called Google Plugin for Eclipse, you need the Gogle WebToolkit SDK from the same update site, too)
- Install Eclipse eGit (optional)
- Check that JDK 1.7 is available and has been set for compilation in Eclipse
- Check that the both JDKs are available (Windows->Preferences->Java->Installed JREs)
- Check that JDK 1.6 has been matched to JavaSE-1.6 and that JDK 1.7 has been matched to JavaSE-1.7 (...>Installed JREs>Execution Environments)
- It is also possible to match the SAPJVM 6 or 7 to the JavaSE-1.6 (for profiling purposes)
- Import all Race Analysis projects from the /java(!!!) subdirectory of the git main folder
- Set the Eclipse target platform to race-analysis-p2-ide-local.target (located in com.sap.sailing.targetplatform/definitions)
- Wait until the target platform has been resolved completely
- In the project com.sap.sailing.gwt.ui create a new subfolder "classes" in the folder WEB-INF
- Rebuild all projects
4. Run the Race Analysis Suite
- Start the MongoDB
- Start the appropriate Eclipse launch configuration (e.g. 'Sailing Server (Proxy)')
- Start the GWT UI
5. Within the Race Analysis Suite
- For TracTrac Events: (Date 27.11.2012) Use Live URI tcp://10.18.22.156:4412, Stored URI tcp://10.18.22.156:4413, JSON URL http://germanmaster.traclive.dk/events/event_20120905_erEuropean/jsonservice.php
- Press List Races
#### Maven Setup
Copy the settings.xml from the top-level git folder to your ~/.m2 directory and adjust the proxy settings accordingly. Make sure the mvn executable you installed above is in your path. Open a shell (preferrably a git bash or a cygwin bash), cd to the java/ subfolder of the git workspace and issue "mvn -fae clean install". This should build the software and run all the tests. If you want to avoid the tests being executed, run "mvn -Dmaven.test.skip=true -fae clean install". If you want to make sure the GWT compiler really re-builds all artifacts, remove all java/com.sap.sailing.gwt.ui/com.sap.sailing.gwt.ui.* directories, then re-build.
#### Further hints
- Configure Eclipse to use Chrome or Firefox as the default browser
- Install the GWT Browser Plugin (Chrome or Firefox) for the GWT Development mode
+93
View File
@@ -0,0 +1,93 @@
# Replicating an SAP Sailing Analytics Server
[[_TOC_]]
### Requirements
Scaling to many (>100) concurrent users following a live sailing race requires providing more than one OSGi server instance. To an extent this is cause by an aspect of our architecture that decides how clients access server data. We use the Google Web Toolkit's (GWT) Remote Procedure Call (RPC) to let the clients request data from the server. These are handled as HTTP POST requests with a servlet composing the response message. This approach makes caching servlet responses to similar requests by a generic HTTP caching / proxy infrastructure difficult.
Using more than one server instance is additionally motivated by the ManyPlayers client architecture. ManyPlayers wants to use our code base but run it on their premises and only be assured that once their instance connects to our "master" instance then all relevant state will be replicated to their instance.
### Server State
The server's state at the current time roughly consists of the following:
* list of races with their tracking data loaded by the server
* a race's state consists of
* RaceDefinition object with name, boat class and event association
* Course object with Waypoints and ControlPoints
* a tracked race's state consisting of
* wind tracks
* raw boat position tracks
* mark position tracks
* mark passings
* race tracking start/finish times
* list of leaderboard groups with their names and descriptions
* list of leaderboards and their association with leaderboard groups
* leaderboard names, columns and the columns' associated tracked races, if any
* discarding rules
* competitor renamings
* explicit score corrections
* disqualification / "max-points" reasons (DSQ, OCS, DNS, DNF, DND, ...)
### Reliability
When a server is configured to be a replica of another "master" server, all updates to the master server must be replicated as quickly as possible to the replica until the replica permanently disconnects from the master server. If the connection breaks temporarily and is restored at a later point in time, all updates that occurred since the connection broke need to be updated to the client.
### Connectivity
Replicated instances may be able to only connect to the master through an HTTP connection. In this case, the master server needs to push updates through an HTTP connection reliably.
### Single Master, Multiple Replicas
For Kiel Week 2012 we want to support a single master server with multiple replicated copies, so-called "replica instances." The replicas are configured for their master node. This configuration doesn't change over the replica's life time. If the master dies, it need to be started again. Replicas then need to recover and re-sync once the new master instance is alive again.
### Concurrency Challenges
Different types of updates to the master server can happen concurrently. Tracking data for multiple races is being received in multiple threads which can execute truly concurrently on a multi-core architecture. Wind data may be received in parallel to receiving other tracking data. Leaderboard and LeaderboardGroup configurations may happen concurrently to all other types of changes. New races may start to get tracked concurrently to the many other server operations. Do we need to ensure that all these events whose effects need to be replicated have to be serialized in an order that is kept the same for all replica to ensure equal resulting states in all replica? What would serializing all these many concurrent events mean to the server's internal concurrency which is important to improve performance?
### Thoughts on Implementation
HTTP push protocols are implemented, e.g., by the Atmosphere framework and by our own, proprietary AbstractHttpPostServlet in the com.sap.sailing.server package. They both use a heartbeat mechanism to re-establish the HTTP connection after it got disconnected. However, both currently don't handle reliable data transmission. If a client is disconnected and re-connects later, it may have missed a number or messages sent while it was disconnected. This would need to be overcome in order to achieve the reliabiliy required for our replication scenario.
### Initial Load
When a replica is first set up and connected to a master server, the master server's current state at some consistent snapshot needs to be replicated to the replica and from then on be kept in sync. How can we produce such a consistent snapshot while understanding which subsequent change events to broadcast to which client?
One approach we're currently testing is using Java Serialization to copy an initial "image" of one server to the next. There are several tricky issues with this approach:
* listeners / observers: several classes offer listeners to be attached; for example, a DynamicTrackedRaceImpl object supports RaceChangeListener objects to attach to it. Similarly, Course objects support CourseListeners to observe the course for changes. Currently, our strategy is to serialize the listener graph together with the object graph they observe by declaring the listener interfaces as "extends Serializable." This unfortunately requires all other listener implementations, even the inner anonymous classes used by test cases, to be serializable too. On the other hand this avoids NotSerializableExceptions.
* non-serializable JDK classes: For example, TrackedRaceImpl uses java.util.Timer to delay cache invalidation. In this fortunate case, making the field transient has no negative effects because the next action that would need the cache to be invalidated will set the timer correctly, so no explicit initialization in readObject() is required. There may be other cases, though, where a readObject() implementation is needed to establish some objects in transient fields after deserialization.
* synchronization: serializing a data structure while it is being modified is a bad idea. The java.util.* collection classes are not thread safe. In those cases where concurrent modifications have to be expected, a writeObject() definition is required that synchronizes the serialization with modifying operations. For example, serializing a TrackedRace object together with its GPSFixTracks and WindTracks needs to synchronize with inbound GPSFix / Wind updates.
### Incremental Updates
Many updates to the server's state are small compared to the overall state. For example, adding a GPS fix to a boat's track is a very small change; so is the modification of a leaderboard name or a leaderboard group's description. Such events need to be replicated incrementally. A message queuing system with persistent message queues may be an implementation option that can guarantee delivery also through HTTP connections.
### Implications for the MongoDB Store
A replica may or may not use the same MongoDB store that the master uses. If it uses the same store, replication events must not update the database. This then also holds for the initial load procedure. On the other hand, if the replica uses its own database instance, even the initial load has to update all structures that require persistent storage into the local database.
Using MongoDB's built-in replication requires a robust TCP connection between the MongoDB replicas. This is hard to establish in case only HTTP connections are possible between the replica and the master.
### Use of a Message Queuing System
An MQ system such as ActiveMQ could serve as basis for the implementation of the replication mechanism. We'd have to wrap all commands in messages. To be clarified: what happens when a client subscribes to a topic? Will it receive messages sent to that topic only after it connected? Or will it also receive older messages that have been queued persistently?
### Design
Changes applied to a server that are relevant for replication have to be applied through a single entry point at RacingEventService, using the command pattern provided by the RacingEventServiceOperation interface. This will allow future extensions towards the use of an Operation Transformation (OT) algorithm in case operations can be performed not only on a single master but throughout the replicas. In addition to the course-grained leaderboard / group creations and editing operations, all tracking data recordings need to pass through this interface in order to be replicated properly.
### RacingEventServiceOperations and RacingEventService
Most of the state changes relevant for replication are triggered through the RacingEventService interface. A few changes, such as the recording of GPS and wind data received, are currently immediately updated into internal data structures, such as a DynamicTrackedRace and the track objects it manages, or the Course object owned by a RaceDefinition object. It would be possible to restructure things in such a way that also the tracking data are funneled through the RacingEventService interface.
A RacingEventServiceOperation describes serializable operations that can be applied to a RacingEventService and which can be transmitted to another server instance (a replica) to be applied to another RacingEventService, eventually leading to equal states of the RacingEventService instances.
Question: Where and how do we ensure that all modifications to a RacingEventService that are relevant for replication are actually turned into RacingEventServiceOperations and then reliably serialized to all replicas?
We could let RacingEventServiceImpl manage the creation of the operation objects whenever a changing method is called. This would require distinguishing between an "external" and an "internal" API where the external API methods create the operations which, when applied, use the internal API methods to perform the actual updates.
Or we restrict the externally visible API of RacingEventService to applying RacingEventServiceOperations. Then, all modifications that are relevant for replication have to go through a RacingEventServiceOperation. This is very hard to ensure because internally all APIs, such as those of Leaderboard and LeaderboardGroup and everything are easily accessible.
Answer: We create the operations already in SailingServiceImpl and only call apply on the RacingEventService whose implementation can then notify an optional replicator of the operations that were executed. At this time it would make sense to remove the fine-grained operations which are now only used by the operation implementations from the RacingEventService interface at least. The operation implementations can cast the RacingEventService object to RacingEventServiceImpl and then access the operations again.
### Parallel Processing of Operations
Many types of operations can potentially be processed in parallel. For example, GPS fixes for different tracked races can be inserted into their respective tracks in parallel. So can edit operations for different leaderboards. A thread pool can be used to process the operations. However, each thread in this pool may need to obtain the monitors for the objects it will modify, and the scheduler that sequentially passes the operations to the thread pool for processing needs to wait for the processing thread's acknowledgement that all monitors required have been obtained before triggering the processing of the next operation. The general problem reminds of the versioning graph as created, e.g., by a git repository. Also, Leslie Lamport's vector clocks come to mind.
Some operations are more tricky to parallelize, particularly if we have to assume race conditions during execution. For example, if an operation creates a leaderboard and a follow-up operation adds a column to the leaderboard just created, the leaderboard creation must be complete before the column can be added. Parallelizing such operations is generally dangerous.
Fortunately, the dangerous-to-parallelize operations seem to be the ones that occur with low frequency (leaderboard creation, score corrections, leaderboard column configuration) and have relaxed requirements regarding the lag with which they have to be replicated. We can therefore partition the data and transactions to be replicated into two categories
### Sequential operations
These encompass leaderboard and leaderboard group creation and manipulation, including score corrections. In a replica these operations must be executed in the same sequence they were executed in on the master. It probably encompasses the creation of a tracked race and its course, we well as the updates to a race course with waypoint additions and mark passings because mark passing events relate to the waypoints.
Paralllel operations
These encompass the receiving of raw sensor data. The order in which this data is re-played doesn't matter. The data is time-stamped, and its history is not rewritten (as maybe the case for the mark passings or for the race tracking start/finish times). Applying these operations to a replica is idempotent, so serializing an initial state doesn't need to be synchronized with the receiving of such data. Duplicate application of these operations to a server doesn't do any harm.