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

Function Calls (FCs) #120

Closed
dfarr opened this issue Jun 4, 2024 · 2 comments
Closed

Function Calls (FCs) #120

dfarr opened this issue Jun 4, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@dfarr
Copy link
Member

dfarr commented Jun 4, 2024

Function calls are objects that represent a function call where the execution may occur locally (in process) or remotely (out of process). These objects describe how a function is being invoked in a particular context, but place no constraints on the function itself - the same function definition be executed locally or remotely.

We need three types of FCs:

  • LFC (local function call)
  • RFC (remote function call)
  • TFC (top level function call, executes locally but with special semantics for the recovery path)
type FC =
  | LFC
  | RFC
  | TFC

These data objects are specified by the user and should include at a minimum:

interface LFC {
  id?: string;
  func: Function;
  args: any[];
  opts: Partial<Options>;
}

interface RFC {
  id?: string;
  func: string | Function;
  args: any[];
  opts: Partial<Options>;
}

interface TFC {
  id: string;
  func: string | Function;
  args: any[];
  opts: Partial<Options>;
}

An FC can be provided as an argument to run, alternatively (and preferably) the user can supply a string or a function to run directly. If a function is provided we infer an LFC, if a string is provided we infer an RFC.

run(fc: FC)
run(func: Function, ...args: any[]) // LFC inferred
run(func: string, ...args: any[])   // RFC inferred

Also, on Resonate we constrict the type of FC to TFC, on Context we constrict the type to LFC | RFC.

run(fc: TFC)       // resonate.run()
run(fc: LFC | RFC) // context.run()

Once we have a user-provided (or inferred) FC, we need to combine this with contextual information to get all necessary information for an invocation. The other sources of information are:

  1. default options (on resonate)
  2. registered function and its options
  3. the parent fc

Once we have this information we can combine an FC with additional information to create an Execution, which can be of one of two types:

type Execution =
  | LE // local
  | RE // remote

interface LE {
  fc: TFC | LFC;

  future: Future;
  resolvers: { resolve: any, reject: any };

  attempt: number;
  counter: number;
  killed: boolean;
}

interface LE {
  fc: RFC;

  future: Future;
  resolvers: { resolve: any, reject: any };

  attempt: number;
  counter: number;
  killed: boolean;
}

Finally, an execution can be run in an internal execute function, this should enable us to rewrite our code to look more sequential (and hopefully be easier to follow). The execute function should roughly do the following:

  • LE
    • create durable promise
    • run the function
    • complete durable promise
    • complete the future
  • RE
    • create durable promise
    • wait for durable promise to be completed
    • complete the future
@dfarr dfarr added the enhancement New feature or request label Jun 4, 2024
@avillega
Copy link
Contributor

avillega commented Jun 6, 2024

At least for now I've been thinking that we might not need two different kinds of executions, instead have a single one that can use any of the three kinds of FC and conditionally run the function or just await the durable promise. I think it might have a similar effect to what is proposed but with a one less layer of indirection and one less type to think about. Let me know what do you think.

@dfarr
Copy link
Member Author

dfarr commented Jun 6, 2024

At least for now I've been thinking that we might not need two different kinds of executions, instead have a single one that can use any of the three kinds of FC and conditionally run the function or just await the durable promise. I think it might have a similar effect to what is proposed but with a one less layer of indirection and one less type to think about. Let me know what do you think.

I'm cool with that! A boolean flag would even do it for the time being since there are only two

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants