Gossip algorithm in distributed systems

I have implemented the gossip algorithm in distributed systems for review.

Gossip class

import random
import socket
import time

class GossipNode:
# hold infected nodes
infected_nodes = []

# initialization method.
# pass the port of the node and the ports of the nodes connected to it
def __init__(self, port, connected_nodes):
# create a new socket instance
# use SOCK_DGRAM to be able to send data without a connection
# being established (connectionless protocol)
self.node = socket.socket(type=socket.SOCK_DGRAM)

# set the address, i.e(hostname and port) of the socket
self.hostname = socket.gethostname()
self.port = port

# bind the address to the socket created
self.node.bind((self.hostname, self.port))

# set the ports of the nodes connected to it as susceptible nodes
self.susceptible_nodes = connected_nodes

print("Node started on port {0}".format(self.port))
print("Susceptible nodes =>", self.susceptible_nodes)

# call the threads to begin the magic

def input_message(self):
while True:
# input message to send to all nodes
message_to_send = input("Enter a message to send:\n")

# call send message method and pass the input message.
# encode the message into ascii
self.transmit_message(message_to_send.encode('ascii'))

while True:
# since we are using connectionless protocol,
# we will use 'recvfrom' to receive UDP message

# remove the port(node), from which the message came from,
# from the list of susceptible nodes and
# add it to the list of infected nodes

# sleep for 2 seconds in order to show difference in time
time.sleep(2)

# print message with the current time.
# decode message so as to print it, as it was sent
print("\nMessage is: '{0}'.\nReceived at [{1}] from [{2}]\n"

# call send message to forward the message to other susceptible(connected) nodes
self.transmit_message(message_to_forward)

def transmit_message(self, message):
# loop as long as there are susceptible(connected) ports(nodes) to send to
while self.susceptible_nodes:
# select a random port from the list of susceptible nodes
selected_port = random.choice(self.susceptible_nodes)

print("\n")
print("-"*50)
print("Susceptible nodes =>", self.susceptible_nodes)
print("Infected nodes =>", GossipNode.infected_nodes)
print("Port selected is [{0}]".format(selected_port))

# since we are using connectionless protocol,
# we will use 'sendto' to transmit the UDP message
self.node.sendto(message, (self.hostname, selected_port))

# remove the node which the message has been sent to,
# from the list of susceptible nodes and
# add it to the list of infected nodes
self.susceptible_nodes.remove(selected_port)
GossipNode.infected_nodes.append(selected_port)

print("Message: '{0}' sent to [{1}].".format(message.decode('ascii'), selected_port))
print("Susceptible nodes =>", self.susceptible_nodes)
print("Infected nodes =>", GossipNode.infected_nodes)
print("-"*50)
time.sleep(2)
print("\n")

# two threads for entering and getting a message.
# it will enable each node to be able to
# enter a message and still be able to receive a message


Nodes: to be run separately

Node_5000

# import the GossipNode class
from Gossip import GossipNode

# port for this node
port = 5000
# ports for the nodes connected to this node
connected_nodes = [5010, 5020]

node = GossipNode(port, connected_nodes)


Node_5010

# import the GossipNode class
from Gossip import GossipNode

# port for this node
port = 5010
# ports for the nodes connected to this node
connected_nodes = [5000, 5030, 5040]

node = GossipNode(port, connected_nodes)


Node_5020

# import the GossipNode class
from Gossip import GossipNode

# port for this node
port = 5020
# ports for the nodes connected to this node
connected_nodes = [5000]

node = GossipNode(port, connected_nodes)


Node_5030

# import the GossipNode class
from Gossip import GossipNode

# port for this node
port = 5030
# ports for the nodes connected to this node
connected_nodes = [5010]

node = GossipNode(port, connected_nodes)


Node_5040

# import the GossipNode class
from Gossip import GossipNode

# port for this node
port = 5040
# ports for the nodes connected to this node
connected_nodes = [5010]

node = GossipNode(port, connected_nodes)

• Can you edit the question to explain what your implementation does? "Gossip" refers to a class of related protocols, not to a particular algorithm, so we need you to tell us a bit more about it. – Gareth Rees Jul 3 '15 at 10:39

First off, you should be using docstrings to describe your functions and classes, not regular comments. A typical docstring might look something like this:

def my_func( ... ):
"""
Describe my_func and it's arguments
in detail here.
"""
...


Secondly, you have a few useless comments, like # sleep for 2 seconds in order to show difference in time, or # call the threads to begin the magic. Comments like these are fairly useless and can be removed.

Other than that, this code is really nice looking!

• Why the downvote? – Ethan Bierlein Jul 3 '15 at 15:28

I think you can make your code better by making the following changes:

• Some of the comments are kind of useless. The code is more or less self explanatory. For example the comment in the following piece of code is useless:

# remove the node which the message has been sent to # from the list of susceptible nodes and # add it to the list of infected nodes self.susceptible_nodes.remove(selected_port) GossipNode.infected_nodes.append(selected_port)

• The following comment is also useless since sendto function's definition clearly says when it is used.

# since we are using connectionless protocol, # we will use 'sendto' to transmit the UDP message

• also try writing shorter comments. You can usually skip the logic behind using the function in the comments, unless it is like a very important part of your code and is not clear from the function name.
• instead of using different codes at different machines, use a single code. Save the port information in a separate file and read the port from that file.

from Gossip import GossipNode FILE_NAME = "randomFile.txt" port = read_port(FILE_NAME) connected_nodes = [5010] ..  I am also removing the comments from this code. It is clear from the code what you are tryign to do and the comments are not adding any useful information.

• i'll cut down on the comments. what would i achieve by saving the port information in a separate file? – Evans Murithi Jul 4 '15 at 10:39
• Saving port information in a file ensures that the same code can be used in all the machines. It is always better to have a code which satisfies DRY (Don't Repeat Yourself) principle. Assume a case when you need to change something in the repeated code. Now you need to make changes in each of those files. If you had used the same file in all machines and lets assume they were syncd using SVN, then you just need to make change in one place. – Pranav Raj Jul 4 '15 at 10:49
• Also in the future if you decide on what port should be used by each system, you just need to change that separate file (maybe the read_port function too) and voila your code will do your job. I hope this helps :) – Pranav Raj Jul 4 '15 at 10:49
• each node (socket) starts on its own port. i can't start two nodes with the same port number. – Evans Murithi Jul 4 '15 at 10:51
• In that case the separate file can store a mapping between the machine's ip with the port number. read_port will just return the port number for that machine. – Pranav Raj Jul 4 '15 at 10:57