|
|
Introduction
This is not a simple drop in plugin that is suitable for any Rails project. Because of the many complexities involved in data merge replication, there are several requirements that have to be met before the plugin actually works.
A short summary includes:
- use of Uniquely Universally Identified IDs (UUID) for every replicable table
- use of UUIDs for each authenticated user of the system
- use of the Engines plugin infrastructure
- use of the Userstamp plugin
Acts As Replica
The plugin per se is installed the usual way: just ./script/plugin install it or svn checkout it from the http://actsasreplica.googlecode.com/svn/trunk/ URL into your project's vendor/plugins folder and you're ready to go.
Once the plugin is installed, run rake install_replica so it will create a needed migration file into your db/migrate folder and modify your config/routes.rb file.
But this is not everything.
UUIDs
Rails assumes the usage of auto-increment integer primary keys, which is obviously good for the majority of apps, but the edge cases, as replication. We could use several techniques to continue using integer keys as defining different ranges for each client. But this can become cumbersome to manage and very prone to errors very fast.
UUIDs are definitely not pretty to the eyes (make ugly URLs), but its very simple to grasp and Rails can deal with them easily. To include this support, we need to install uuidtools:
gem install uuidtools
Authentication
A replication system has an inherent need for authenticated users, otherwise we have no means to control who's sending data, nor when. And each User has to be uniquely identifiable through a UUID as well. In the migration file that the plugin creates, I suggest the definition of a table called 'users'. It has to have this exactly name and the following minimum columns:
create_table :users, :id => false do |t| t.column :id, 'varchar(36) primary key', :null => false ... t.column :guid, :string, :limit => 36, :null => false t.column :last_synced, :timestamp t.column :created_by, :string, :limit => 36 t.column :updated_by, :string, :limit => 36 t.column :created_at, :timestamp t.column :updated_at, :timestamp end
Notice that the created_by and updated_by columns here and in all other replicable tables have to be varchar(36) to hold a complete UUID in string format. All foreign keys will also have to be varchar(36).
Also notice that I've put a UUID primary key and a separate 'guid' UUID column as well. They are different keys altogether but both identify the same user. This is because of the handshake process I implemented that involves a simple challenge-response. The first leg of the process involves sending the user ID and getting back the challenge key. The second leg involves generating a response based on the challenge and the 'guid' user key, so a man-in-the-middle could get the user ID and guess the response if this key was involved, so I replaced it with a 'hidden' key that only the peers know about.
The server knows the 'guid' key because its stored in a 'remote_clients' table. This table is only used by the 'server'. The clients will have this table (as the app code is supposed to be the same in all peers). This table also acts as the server logger for syncing timestamps for each client, so the server 'knows' where to restart the next time any of the peers connect back.
Generator
You will need to install certain files within your Rails project. To do that just call this:
./script/generate replicator
It will copy the syncs_controller.rb, perform_sync.rhtml and perform_upgrade.rhtml views, replicator.rb and upgrade.rb into lib/daemons, a create_replicas.rb migration file and a fancy animated gif spinner in public/images. After that make sure you update your config/routes.rb including the following:
map.connect "/syncs/:action.:format", :controller => "/syncs" map.connect "/syncs/:action", :controller => "/syncs"
UserStamp
Each row in each replicable table has to be trackable. Rails already takes care of the created_at and updated_at timestamp columns, but now we need to identify who made the changes. Because of the nature of the replication process, I embedded and modified the UserStamp plugin that was usually installed from here:
./script/plugin install svn://delynnberry.com/code/plugins/userstamp/trunk
Do not install the original plugin, though.
Here comes yet another thing you will have to add manually. As I can't possibly know which authentication plugin you will use (I am using RESTful Authentication) you will have to modify your User model manually, albeit with an easy modification:
class User < ActiveRecord::Base acts_as_auditor ... end
Then you will have to modify your ApplicationController as well:
class ApplicationController < ActionController::Base acts_as_userstamp ... end
This assumes that your authentication system will record the logged in user UUID in session[:user]. And finally, each replicable model of yours will have to have something like this:
class YourModel < ActiveRecord::Base acts_as_replica ... end
Conclusion
Having said all that, you should now have a complete replicable system. You now can go on to know how this works and how you can test it. Go on to read HowToTest this.
Sign in to add a comment
