class Pokan::Server

Gossip server class, using the Pokan::Peer class. This is the main class instantied by user code.

When a server class is created, the user should set:

The user can also use epoll for faster I/O notification, if the server is running on a Linux system.

Sample usage is shown below:

s = Pokan::Server.new
s.gossip_every(1) #=> tells the server to gossip every second
s.use_epoll #=> I'm in a Linux system

s.store 'k', 'v'

e.on :new_key do |key, value|
  puts "What a great key I just got: #{key}!"
end

# more event related code...

e.start #=> last command, blocks execution

Constants

AFTER_EVENTS
BEFORE_EVENTS
ON_EVENTS

Attributes

gossip_interval[R]

Public Class Methods

new(address) click to toggle source

Creates a new gossip server. You must pass the machine’s IP address in the network. This information will be used when changing information with other peers running Pokan. This also set some defaults, namely:

  • server will bind TCP and UDP port number 5555. If you have something else running, you must set them like:

    server.tcp_port = 9876
    server.udp_port = 9989
    
  • epoll will not be used (you might want to set it if you are in a Linux system. See Pokan::Server#use_epoll

  • The server will comunicate with other peers _every second_

Of course, all of these are customizable to your particular needs.

Calls superclass method Pokan::Peer::new
# File lib/pokan/server.rb, line 62
def initialize(address)
  @event_handler = EventHandler.new
  @seed_address, @seed_port = '', ''
  @periodics = []
  self.address = address
  self.udp_port = 5555
  super()
  initialize_ivs
end

Public Instance Methods

after(event, &block) click to toggle source

Same as the Pokan::Server#before, but it also supports the :sync event. Use this method to run some code after the server gossips with a random peer or after it syncs with a seed (if you passed one on initialization. See Pokan::Server#start).

# File lib/pokan/server.rb, line 133
def after(event, &block)
  event_name = prepend('after_', event)
  register_event(event_name, &block) if AFTER_EVENTS.include? event
end
before(event, &block) click to toggle source

Adds behaviour before a given event occurs. For now, the only event supported is :gossip. Use this method if you want some code to run every time before the server gossips with a random peer.

# File lib/pokan/server.rb, line 124
def before(event, &block)
  event_name = prepend('before_', event)
  register_event(event_name, &block) if BEFORE_EVENTS.include? event
end
every(seconds, &action) click to toggle source

Adds behavior to the server, to be executed periodically (every given amount of seconds).

server.every 2 do
  puts "Heya!"
end

Every 2 seconds “Heya!” will be printed out.

# File lib/pokan/server.rb, line 147
def every(seconds, &action)
  @periodics << { :period => seconds, :action => action }
end
gossip_every(seconds) click to toggle source

Use this method to specify the time interval you wish to use for changing gossip messages among peers. Default is 1 second.

# File lib/pokan/server.rb, line 86
def gossip_every(seconds)
  @gossip_interval = seconds
end
on(event, &block) click to toggle source

Defines the behaviour you want the server to have on different events. For now, you can define your behaviour for the following events:

  • :new_key - This will be called whenever a new_key is inserted (and there was no previous value for that)

  • :key_changed - This is called when a value for a certain key is updated (a value with greater timestamp)

  • :key_removed - The name speaks for itself

For all the events, both the key and the value are passed to the block.

Example:

server = Pokan::Server.new('10.10.10.8')
server.on :new_key do |key, value|
  puts "New key #{key} was inserted with value #{value}!"
end
# File lib/pokan/server.rb, line 117
def on(event, &block)
  register_event(event, &block) if ON_EVENTS.include? event
end
start(options = {}) click to toggle source

This method starts the server, which will bind specified TCP and UDP ports (or 5555 by default). Optionally, you can pass the address of another pokan instance (a seed), and the server will automatically send a message to the seed and update its data. If no seed information is passed, the server will start with no data and assume it is a seed.

Example:

server = Pokan::Server.new('10.10.10.8')
server.start :gossip_with => '10.10.10.9:5555'

In the example above, the server will try to contact (via TCP) a pokan server running on 10.10.10.9, port 5555, and update its data accordingly.

# File lib/pokan/server.rb, line 166
def start(options = {})
  seed = options[:gossip_with]

  if seed.nil?
    act_as_seed
  else
    @seed_address, @seed_port = parse_host_and_port(seed)
    say_hello(@seed_address, @seed_port)
    build_trap

    @event_handler.emit :before_gossip, :with => [@seed_address, options[:redis]]
    sync_with(@seed_address, options[:redis])
    @event_handler.emit :after_sync, :with => [1]
  end

  start_event_loop
end
stop() click to toggle source

Finishes the server

# File lib/pokan/server.rb, line 185
def stop
  kill
  EventMachine.stop
end
store(key, value, timestamp=Time.now) click to toggle source

Stores a key, value pair that will be shared among all the peers in the network.

Calls superclass method Pokan::Peer#store
# File lib/pokan/server.rb, line 92
def store(key, value, timestamp=Time.now)
  super
  save
  @event_handler.emit :new_key, :with => [key, value]
end
use_epoll() click to toggle source

Call this method if you want to use epoll in your Linux system. Warning: this will not throw an exception nor will warn you if you use this method in a non-Linux machine. You might face errors when you try to start the server, though.

# File lib/pokan/server.rb, line 76
def use_epoll
  @epoll = true
end
using_epoll?() click to toggle source
# File lib/pokan/server.rb, line 80
def using_epoll?
  @epoll
end

Private Instance Methods

build_trap() click to toggle source
# File lib/pokan/server.rb, line 239
def build_trap
  trap('INT') { prepare_to_die }
  trap('TERM') { prepare_to_die }
  trap('QUIT') { prepare_to_die }
end
gossip() click to toggle source
# File lib/pokan/server.rb, line 259
def gossip
  EventMachine.add_periodic_timer(@gossip_interval) {
    query = Query.new(Peer)
    peer = query.where(:random => true, :role => ['seed', 'peer'], :not => [id],
                       :status => 'alive').first

    unless peer.nil?
      @event_handler.emit :before_gossip, :with => [peer.address, peer.udp_port, digest_message.size]
      Network.udp(digest_message, peer.address, peer.udp_port)
      @event_handler.emit :after_gossip, :with => [peer.address, peer.udp_port]
    end
  }
end
initialize_ivs() click to toggle source

Sets default variables, such as:

  • TCP and UDP ports: 5555

  • do not use epoll

  • gossip every second

# File lib/pokan/server.rb, line 196
def initialize_ivs
  self.tcp_port     = 5555
  @epoll            = false
  @gossip_interval  = 1
end
parse_host_and_port(address) click to toggle source
# File lib/pokan/server.rb, line 206
def parse_host_and_port(address)
  address.split ':'
end
prepare_to_die() click to toggle source
# File lib/pokan/server.rb, line 249
def prepare_to_die
  say_goodbye(@seed_address, @seed_port)
  @event_handler.emit :shutdown
  exit 0
end
prepend(string, event) click to toggle source
# File lib/pokan/server.rb, line 210
def prepend(string, event)
  event_str = string + event.to_s
  event_str.to_sym
end
register_event(event, &block) click to toggle source
# File lib/pokan/server.rb, line 202
def register_event(event, &block)
  @event_handler.register(event, &block)
end
register_periodics() click to toggle source
# File lib/pokan/server.rb, line 231
def register_periodics
  @periodics.each do |callback|
    EventMachine.add_periodic_timer(callback[:period]) {
      callback[:action].call
    }
  end
end
say_goodbye(address, port) click to toggle source
# File lib/pokan/server.rb, line 255
def say_goodbye(address, port)
  Network.tcp(goodbye_message, address, port)
end
say_hello(address, port) click to toggle source
# File lib/pokan/server.rb, line 245
def say_hello(address, port)
  Network.tcp(hello_message, address, port)
end
start_event_loop() click to toggle source
# File lib/pokan/server.rb, line 215
def start_event_loop
  EventMachine.run {
    gossip
    register_periodics
 
    RequestHandler.address = address
    RequestHandler.port    = udp_port

    @event_handler.emit :start, :with => [address, tcp_port, udp_port, @epoll, 
      @gossip_interval, @seed_address, @seed_port]

    EventMachine.open_datagram_socket address, udp_port, RequestHandler
    EventMachine.start_server address, tcp_port, RequestHandler
  }
end