Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Masterslave algorithm #234

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open

Masterslave algorithm #234

wants to merge 7 commits into from

Conversation

nicolew02
Copy link
Contributor

@nicolew02 nicolew02 commented Feb 21, 2020

Master slave Algorithm

Algorithm to determine which slave process is the master process.
Each process starts up thinking it's the master and as it connects to other processes of the same type, it negotiates. Process making outbound connection initiates negotiation.

Checklist

  • checkifmaster function to return dictionary of process details
  • table stored in each process that holds own and other processes details
  • table updated with which process is master and with new/dropped connections
  • if master process drops, renegotiation of processes to determine new master (.z.pc override)
  • init function to initialise
  • sendtoslaves function - publish msg to all slaves (only master to do this)
  • sendtomaster function - propagates message to the master

@nicolew02 nicolew02 added the wip Work In Progress label Feb 21, 2020
@nicolew02 nicolew02 self-assigned this Feb 21, 2020
@jonnypress jonnypress removed the request for review from jonathonmcmurray February 21, 2020 11:23
masterslave.q Outdated
details:{`procname`starttimeUTC`ismaster!(.proc.procname;.masterslave.start;1b)}

//get details of procname provided
getdetails:{[processname] (first exec w from .servers.SERVERS where procname like processname) ".masterslave.details[]"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When doing IPC can you avoid strings here, i.e. use
(.masterslave.details;)
This should be an async request, but we'll do that later.

I think this should do something on the remote side too i.e. a getdetails call from A->B causes both sides of the negotation to know about each other. So when B receives this call, it should use the details passed in from A to update it's own status

masterslave.q Outdated

//update .masterslave.statustable with other proc details and update ismaster col to determine which process is master
addmember:{[processname]
update ismaster:0b from (`.masterslave.statustable upsert .masterslave.getdetails[processname],(enlist `handle)!(enlist first exec w from .servers.SERVERS where procname like processname)) where starttimeUTC<>min starttimeUTC}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you split this out across multilines / separate statements. Kinda hard to read

masterslave.q Outdated


// the list of processes to connect to
.servers.CONNECTIONS:`lb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be appended to the list of connections, and do it within an init[] function. Also just use .proc.proctype rather than `lb

masterslave.q Outdated

// custom function, this is invoked when a new outbound connection is created
// to be customised to invoke negotiation of processes
.servers.connectcustom:{.lg.o[`connect;"found new connection"]; show x}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so need to implement this. When it receives a new connection from the discovery service or on a retry, it needs to see if there are any procs of the same proctype that it needs to communicate with to find out who is master.

This definition also needs to include any prior definitions (i.e. to not override previous definitions)

.servers.connectcustom:{[f;x] ... your code in here ...; f@x }@[value;`.servers.connectcustom;{{}}]

This should also only be done in the init function

Use this same approach for .z.pc (capture the previous definition, add your own)

masterslave.q Outdated
start:.z.p

//set details dict with own details
details:{`procname`starttimeUTC`ismaster!(.proc.procname;.masterslave.start;1b)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think probably for now we can drop the ismaster value
We can just pass the name and the start time, and then both sides of the negotiation can work out which is the master (which might be neither of them)

masterslave.q Outdated
.masterslave.checkifmaster[];
};
// create connections
.servers.startup[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having .servers.startup[] in here is fine for testing purposes, but in final version this shouldn't be in here, i.e. someone who loads this should do

.ms.init[]
and then call
.servers.startup[] later in their code

Then init function should also be within the .masterslave namespace

masterslave.q Outdated

\d .masterslave

//table scehma of connected lb processes with ismaster status
statustable:([handle:`int$()] procname:`symbol$();starttimeUTC:`timestamp$();ismaster:`boolean$())
statustable:([handle:`int$()] procname:();starttimeUTC:`timestamp$();ismaster:`boolean$());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you change procname to ()? symbol is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was getting type errors because I kept switching between symbol and string procnames.
Will change so there will only be sym procnames

masterslave.q Outdated

//is the process itself the master
ammaster:{exec ismaster from .masterslave.statustable where handle=0}
ammaster:{exec ismaster from .masterslave.statustable where handle=0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably returning a list, should return an atom

masterslave.q Outdated
.servers.startup[];
.masterslave.addmember each string exec procname from .servers.SERVERS where proctype like "lb", not null w
}
(.masterslave.addmember')[string exec procname from .servers.SERVERS where proctype like "lb", not null w];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't hardcode the proctype in here. Should just be

where proctype=.proc.proctype
.i.e proctype is the same as the local process

masterslave.q Outdated

//.z.pc call
//.z.pc call
.z.pc:{.masterslave.pc[x y;y]}.z.pc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to wrap this in an error trap (in case .z.pc isn't initially defined) i.e.
.z.pc:{.masterslave.pc[x y;y]}@[value;`.z.pc;{{[x]}}]
Also as per comment above, better to have .masterslave.pc only take one param, and then .z.pc is

.z.pc:{x y; .masterslave.pc[y]}@[value;`.z.pc;{{[x]}}]

.z.pc should only be defined within init[]

masterslave.q Outdated
//tell other alive processes to renegotiate who the master is

//tell other alive processes to renegotiate who the master is
pc:{[result;W]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pc should probably only take one param here, W

masterslave.q Outdated
// custom function, this is invoked when a new outbound connection is created
// to be customised to invoke negotiation of processes
.servers.connectcustom:{.lg.o[`connect;"found new connection"];
.masterslave.checkifmaster[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get a new connection in, then we only want to get the details for that single new connection. WE don't want to re-run the whole thing for all connections, which I think is what is happening here.

Copy link
Contributor

@jonnypress jonnypress left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it is coming together.
Can you fix up as per the comments, and then we may switch it over to use async communication (I'll show you how to do that after)

masterslave.q Outdated
//remove dropped connection from statustable
//tell other alive processes to renegotiate who the master is
pc:{[W]
.masterslave.deletedropped[W];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need a check in here to not do this if W=0
When the process starts up in the background it will do a close event on the 0 handle, which we don't want to remove from the table.

masterslave.q Outdated

// custom function, this is invoked when a new outbound connection is created
// to be customised to invoke negotiation of processes
.servers.connectcustom:{[f;x] .masterslave.checkifmaster[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will work, but there is still too much work going on here (too much IPC)
Basically .servers.connectcustom will get invoked when a new connection is made, and it will be passed in (as the x argument in this case) the new connections. So, you only need this process to negotiate with those new processes, not all the old ones too. In this version, if there are 10 connections, and a new one arrives, then the way you have it every process will make 11 outbound requests, whereas really they only need to be making one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is f the function to be called on the new connection handlex?

And instead of running .ms.checkifmaster when a new connection is made,
I should call .ms.addmember with the new connection procname?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is f the function to be called on the new connection handlex?
Yes. f is the pre-existing definition of .servers.connectcustom (which may be nothing)
I should call .ms.addmember with the new connection procname
I think so

masterslave.q Outdated

//get details of procname provided
getdetails:{[processname]
(first exec w from .servers.SERVERS where procname like raze string processname,not null w) (`.masterslave.details;[])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you doing raze string processname here?
Also why a like rather than an = if it is a symbol?
(same comment for line below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will change it to =, I think it was cause I was originally using strings

masterslave.q Outdated
//update .masterslave.statustable with other proc details and update ismaster col to determine which process is master
addmember:{[processname]
`.masterslave.statustable upsert .masterslave.getdetails[processname],
(`handle`ismaster)!(first exec w from .servers.SERVERS where procname like processname,not null w;1b)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this return value used for anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as in when it returns `.masterslave.statustable?
This func is just to upsert process details dictionary into the .ms.statustable, I'm not aware of any other return value?

@tsmyth-aquaq
Copy link
Contributor

Lots of terminology to update in here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wip Work In Progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants