Inspiration

Following is an incident which happened before this hack existed: I was taking a machine learning course in school and for one of our project had to train 100 random forests for a training data set. We had 5 people in our team, all with okayish computers. We couldn't find any easy way to setup a distributed jupyter notebook, that could train 20 random forests on each of our PCs and eventually aggregate all of them on one root PC. We ended up wasting 5x time by training everything on a single PC. Setting up an AWS cluster would turn out to be too hassle-some with the nearing deadline and we were discouraged by the not so user-friendly process of sending/receiving data from one node of the cluster to another.

We wanted a cheap, easy to setup distributed computing solution over the internet for our 5 PCs which had an easy to use API to send and receive data between those 5 PCs

What it does

A python based CLI application, that enables users over the internet to share their computing power to run other people's and their own python codes.

We use a built-in virtual currency by the name, wni-money to control demand and supply of computing power.

1) Register a new user using the command:

wni register --user sid

  • Created a new user with username sid
  • Initially a new account is given free 1000 wni-money

2) Let's take an example, that a user wants to find the all the perfect numbers, i.e those numbers whose sum of factors is equal to the number itself in the range [1, 9999] using 3 other PCs

3) I.e the user (user name = sid) wants to run a specific code (perfect_numbers.py) on a specific number of PCs (N = 3), they can do so by writing the following command:

wni request PerfectNumbers perfect_numbers.py 3 -user sid -rate 2 -seconds 30

Generically: wni request ROOM_ID CODE_FILE NUMBER_OF_PCS -user USER_ID -rate RATE -seconds SECONDS

  • Created a room by the name PerfectNumbers
  • Here RATE refers to the amount of wni-money you are willing to spend per second per PC that lends you computing power.
  • Here SECONDS refers to the amount of seconds for which you will be computing on each PC (including yours), i.e ideally perfect_numbers.py should finish its execution within 30 seconds on all the PCs
  • The total money spent by this request = rate * seconds * number_of_pcs_borrowed = 2 * 30 * 3 = 180

4) The user would have also needed to write the code to solve the problem (in perfect_numbers.py) using our provided function-calls:

  • send_data(node_id, data) sends data from current node to node_id, here data can be any type of variable that is convertible in binary
  • received_data = receive_data(node_id) receives data from node_id
  • Both of these functions are synchronous in nature and if multiple messages are sent from a given node to another given node, then those multiple messages will follow FIFO (first-in first-out) ordering.
  • In the code MY_ID will denote the current users own id which can range from 0 to N (where N = 3, for 1a example)
  • MY_ID is set to 0 for the root node, that is the one which calls the wni request

perfect_numbers.py:

# Prints the list of those numbers that are perfect
# (i.e the sum of whose proper factors is equal to the numer itself)
# in the range [1, 9999], using 3 other PCs (for demo: All 3 are running as independent droplets on DigitalOcean)
N = 9999
P = 3
print("MY_ID", MY_ID)
def is_perfect(x): # a boolean function to check if a number is perfect or not
    sum = 0
    for i in range(1, x):
        if((x % i) == 0):
            sum = sum + i
    return (sum == x)

if MY_ID == 0: # root code of sending ranges
    for i in range(0, P):
        l = (N // P) * i + 1
        r = (N // P) * (i + 1)
        send_data(i + 1, (l, r)) # send the tuple (l, r) to node i + 1.
    ans = []
    for i in range(0, P):
        p = receive_data(i + 1) # receive lists from all the 3 slaves
        ans = ans + p # concatenate them
    print("List of perfect numbers in range [1, 9999]:", ans)
else: # slaves code which do the computation
    (l, r) = receive_data(0) # receives the tuple from node 0.
    ans = []
    for i in range(l, r + 1):
        if is_perfect(i):
            ans.append(i)
    print("List of perfect numbers found in range [", l, ", ", r, "]:", ans)
    send_data(0, ans) # send list to node 0

To look for more sample codes on how a user will use the send_data and receive_data see this

5) Another user (user name = alice) can potentially run a show command on their PC, to see how much wni-money they have and the detail of all the active requests/rooms:

wni show -user alice

Generically: wni show -user USER_ID

wni show command output image

6) Then some users (actually 3) can join this room (PerfectNumbers) using the following command:

wni join PerfectNumbers -user alice

Generically: wni join ROOM_ID -user USER_ID

Once all 3 users connect, the code perfect_numbers.py is run on all the PCs and all the 3 users earn (rate * seconds) = 2 * 30 = 60 amount of wni-money each.

Demo Video

How I built it

  • The server is written in Python3 and setup on a digitalocean server. (The --dev mode of the application assumes the serve is running on localhost)

  • The client side CLI application is also written in Python3 (uses Click library to create the command line interface)

  • Both communicate using Sockets.IO an abstraction over GET/POST requests on HTTP, which provide the benefits of socket style network calls

Challenges I ran into

  • Running a python code from another python code, and being able to stop it at will
  • Handling some tricky cases. Example: When one of the joined slave user exits before the allocated time runs out, then this slave user should pay all the money instead of the root (since this slave messed up the computation of root)

Accomplishments that I'm proud of

  • The application works :)
  • Soloed a 24-hr hackathon first time + Better time management than my previous attempts

What I learned

  • Making heavily inter-twined network calls requires intricacy in not getting stuck in mutually recursive calls AND remembering not to miss any calls (+ hard to debug)
  • Learnt setuptools, pickle, click and socket.io libraries in Python3
  • Learnt basics of how to spin up and use a droplet/VM on DigitalOcean

What's next for We Not I (wni)

  • Use a 2N Software Engineering approach, so as to make the distributed computing more trustable, where for each node, actually 2 PCs do the work. Incase any one of PCs of that pair fails, the root will still remain unaffected.

  • Make the send_data(...,...) and receive_data(...) functions, asynchronous and/or add support to do send broadcast to all the other nodes from a given node

  • Fix security concerns:

    • A proper (user, password) system instead of just (user) (Done partially, because of lack of time)
    • Allow only a restricted subset of Python to be run on other people's machines, otherwise the root user can potentially make malicious system calls (Not done, since currently a prototype)

Credits: "We Not I" is also the motto of a computing club (Exun Clan) at a High School (DPS RK PURAM)

Share this project:

Updates

Submission history