Author [EN] [ZH] [ES] [PT] [IT] [DE] [FR] [NL] [TR] [SR] [AR] [RU] [EN] [ZH] [ES] [PT] [IT] [DE] [FR] [NL] [TR] [SR] [AR] [RU] [EN] [ZH] [ES] [PT] [IT] [DE] [FR] [NL] [TR] [SR] [AR] [RU] Topic: b solution for hot standby(From BitSuperLab)  (Read 442 times)

0 Members and 1 Guest are viewing this topic.

Offline alt

b solution for hot standby(From BitSuperLab)
« on: July 30, 2014, 05:53:35 AM »

here is another solution for hot standby, detected with a heartbeat signal.

master and slave exchange  some data in real time, include: blockchain_head_block_num, network_num_connections.
they'll change block production flag automatic, depend on  a rule to select which one should active.

we use a pusher service from PubNub to exchange data, this can avoid open a public service, and can still run behind a tor net.

so you need to register a free account in PubNub if you decide to use this. the demo account maybe not stable.
here is the command
Code: [Select]
./heartbeat.py master  # run on master side
./heartbeat.py slave  # run on slave side
here is the source code heartbeat.py,  you need to install pubnub first
Code: [Select]
## www.pubnub.com - PubNub Real-time push service in the cloud.
# coding=utf8

## PubNub Real-time Push APIs and Notifications Framework
## Copyright (c) 2010 Stephen Blum
## http://www.pubnub.com/

#### required:
#### aptitude install python-pip
#### pip install pubnub==3.5.2

import requests
import json
import os

from pprint import pprint

import sys
from Pubnub import Pubnub
import datetime, threading, time

node_type = len(sys.argv) > 1 and sys.argv[1] or 'master'
if node_type == 'master':
    node_type_other = 'slave'
else:
    node_type =  'slave'
    node_type_other = 'master'

print "node typ is " + node_type

config_data = open('config.json')
config = json.load(config_data)
config_data.close()
node_state = {'master':{'connect_num':100,'block_num':100000000}, 'slave':{'connect_num':0,'block_num':0}}

#pprint(config)

## -----------------------------------------------------------------------
## function about bts rpc
## -----------------------------------------------------------------------
auth = (config["bts_rpc"]["username"], config["bts_rpc"]["password"])
url = config["bts_rpc"]["url"]

def get_state():
    global node_state
    headers = {'content-type': 'application/json'}
    info = {
        "method": "get_info",
        "params": [],
        "jsonrpc": "2.0",
        "id": 1
        }
    info_res = requests.post(url, data=json.dumps(info), headers=headers, auth=auth)
    info_json = json.loads(vars(info_res)["_content"])
    node_state[node_type]["block_num"]  = info_json["result"]["blockchain_head_block_num"]
    node_state[node_type]["connect_num"]  = info_json["result"]["network_num_connections"]
    node_state[node_type]["wallet_next_block_production_timestamp"] = info_json["result"]["wallet_next_block_production_timestamp"]

def is_active():
    global node_state
    return node_state[node_type]["wallet_next_block_production_timestamp"] != None

# unlock the delegate first in nodes
def set_delegate_production(enable):
    if is_active() == enable:
        return
    headers = {'content-type': 'application/json'}
    set_delegate = {
        "method": "wallet_delegate_set_block_production",
        "params": ["ALL", enable],
        "jsonrpc": "2.0",
        "id": 1
        }

    set_res = requests.post(url, data=json.dumps(set_delegate), headers=headers, auth=auth)
    set_json = json.loads(vars(set_res)["_content"])
    print 'set_delegate_production', enable, 'result:',set_json
def switch_active(type):
    #print "switch active to", type
    if node_type ==  type:
        set_delegate_production(True)
    else:
        set_delegate_production(False)

def select_active():
    # TODO check the next bock production timestamp of antoher node, require enough time (at least 2 min) for switch
    if node_state["master"]["connect_num"] < 20 and node_state["slave"]["connect_num"] > 22:
        switch_active("slave")
    elif node_state["master"]["block_num"] + 2 < node_state["slave"]["block_num"]:
        switch_active("slave")
    else:
        switch_active("master")



## -----------------------------------------------------------------------
## function about pubnub
## -----------------------------------------------------------------------
# Asynchronous usage
def callback(message, channel):
    global node_state
    node_state[node_type_other] = message
    print datetime.datetime.now(), 'receive', node_state[node_type_other]

def error(message):
    print("ERROR : " + str(message))

def state_publish():
    global node_state
    global next_call
    get_state()
    print datetime.datetime.now(), 'publish', node_state[node_type]
    pubnub.publish(node_type, node_state[node_type])
    select_active()
    next_call = next_call + 10
    threading.Timer( next_call - time.time(), state_publish).start()

## -----------------------------------------------------------------------
## Initiate Pubnub State
## -----------------------------------------------------------------------
publish_key = config["pubnub_auth"]["publish_key"]
subscribe_key = config["pubnub_auth"]["subscribe_key"]
secret_key = config["pubnub_auth"]["secret_key"]
cipher_key = config["pubnub_auth"]["cipher_key"]
ssl_on = False
pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key,
    secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on)

pubnub.subscribe(node_type_other, callback=callback, error=error)

next_call = (int (time.time() / 10)) * 10 + 5
state_publish()

here is the config file: config.json
Code: [Select]
{
  "pubnub_auth": {
    "_comment": "demo is for test, you should register from https://admin.pubnub.com, and set this key to yourself",
    "publish_key" : "demo",
    "subscribe_key" : "demo",
    "secret_key" : "demo",
    "cipher_key" : ""
  },
  "bts_rpc": {
    "url": "http://localhost:9989/rpc",
    "username": "user",
    "password": "pass"
  }
}
« Last Edit: July 30, 2014, 05:55:44 AM by alt »

Offline xeroc

  • Board Moderator
  • Hero Member
  • *****
  • Posts: 12242
  • ChainSquad GmbH
    • View Profile
    • ChainSquad GmbH
  • BTS: xeroc
  • GitHub: xeroc
Re: b solution for hot standby(From BitSuperLab)
« Reply #1 on: July 30, 2014, 04:16:14 PM »
+5% .. much more advanced than my solution :) thxn for sharing

never heard of pubnub .. gonna check out
Give BitShares a try! Use the http://testnet.bitshares.eu provided by http://bitshares.eu powered by ChainSquad GmbH

Offline emski

  • Hero Member
  • *****
  • Posts: 1283
    • View Profile
    • http://lnkd.in/nPbhxG
Re: b solution for hot standby(From BitSuperLab)
« Reply #2 on: July 30, 2014, 08:48:07 PM »
All these data exchanges only increase the chance to sign two different blocks.
It is better to miss blocks in 0.1% of the cases than sign two (different) blocks at once with 0.0001% chance. The latter should get you automatically fired (at least this is what bytemaster said several times).

Before setting up the slave you need to be 100% sure that the master is not going to sign a block and it has NOT already sent it.

Offline alt

Re: b solution for hot standby(From BitSuperLab)
« Reply #3 on: July 31, 2014, 12:14:00 AM »
All these data exchanges only increase the chance to sign two different blocks.
It is better to miss blocks in 0.1% of the cases than sign two (different) blocks at once with 0.0001% chance. The latter should get you automatically fired (at least this is what bytemaster said several times).

Before setting up the slave you need to be 100% sure that the master is not going to sign a block and it has NOT already sent it.
Thanks for the informations that I must have missed.
If it's forbid one day, we'll change the rule, to make sure there is only 1 active.

 

Google+