Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #30280

Re: RPC/RMI in heterogeneous environments

From Don Y <blockedofcourse@foo.invalid>
Newsgroups comp.arch.embedded
Subject Re: RPC/RMI in heterogeneous environments
Date 2021-01-26 00:06 -0700
Organization A noiseless patient Spider
Message-ID <ruof2e$711$1@dont-email.me> (permalink)
References <rudab1$9ol$1@dont-email.me> <n0ms0gpr41kj4ov08b1gu2jo61pavou9hc@4ax.com>

Show all headers | View raw


Hi George,

You guys keeping safe?  I've spent the day documenting the
vaccination process for my neighbors.  Sure would have been
nice if that information was *published*!  (moronic IT
people making process decisions...)

*Snow* (dusting) expected tomorrow.  Yay!  Maybe a break
from the allergies!!  (though sad news for the citrus <shrug>)

On 1/24/2021 11:58 PM, George Neuner wrote:
> On Thu, 21 Jan 2021 18:38:27 -0700, Don Y
> <blockedofcourse@foo.invalid> wrote:
> 
>> I postpone automatic type conversion to the *servant*, rather
>> than embedding it in the client-side stub.  My reasoning is
>> that this allows a wider window for the targeted node to change
>> *after* the RPC/RMI invocation and before it's actual servicing.
>>
>> I.e., it would be silly to convert arguments for a target node
>> of type X, marshall them and then, before actual transport,
>> discover that the targeted *service* now resides on node Y
>> (which is of a different "character" than X).  That would require
>> unmarshalling the already converted arguments (the originals having
>> been discarded after the first conversion), converting them from X
>> to Y representations, remarshalling and facing the same potential
>> "what if it moves, again?"
> 
> Or it means choosing a particular 'server' before marshalling the
> data.  Although nowadays heterogenous hardware is handled with
> agnostic protocols, in the past it was not unusual to use a 3-way
> handshake when dealing with varying hardware.  The 2 sides could
> negotiate the protocol rather than it being fixed.

The client has no control over the server; wherever the object is
"backed" is where the RMI was be destined.  It would be like
having the ability to pick which server handles your bank
account...

And, as that backing can change WHILE the stub is executing (or,
while a packet is waiting to gain access to the wire), the earlier
you make a decision about a binding, the greater the chance of
that decision needing to be "rethought" before the packet
actually *hits* the wire.

[I can handle these conditions but they mean another hop
in the transport as a TEMPORARY zombie proxy is created on
the previous hosting node to catch "late arrivals" and forward
them to the new node.  This just moves the goal-posts a bit.
But, that zombie is intentionally short-lived just to cover
the race condition]

The goal in all this is to minimize the performance hit for the
"local" case; if everything an app needs resides on the local
node, then it shouldn't be encumbered by all this extra mechanism;
just the cost of a streamlined /I/PC!

>> However, this defers actual *checking* the data until after
>> transport.  It also means transport errors take a higher priority
>> than data type errors (cuz the data aren't checked until they've
>> actually been transported!)
>>
>> Discussing this with colleagues, it seems a smarter approach might
>> be to split the activities between client- and server- side stubs.
>> E.g., check the data in the client-side stub so any errors in it
>> can be reported immediately (without waiting or relying on transport
>> for that to occur).  Then, let the servant sort out the details of
>> conversion KNOWING that the data are "valid"/correct.
>>
>> [The downside of this is the servant must bear the cost of the
>> conversion; so, targeting an action to a resource-starved node is
>> costlier than it might otherwise have been.]
> 
> Although admittedly unusual, I can see RPC allowing, e.g., 'integer'
> while not requiring any particular width, or 'float' while accepting
> integer values and converting.  It would allow the client to send
> whatever value it has - as long as it makes sense in context - even if
> the server-side function might be expecting something different.

I insist that a conversion never lose information.  So, float to
int is out.  And, int to float can also be out (depending on the
supported int values and the range of the float).

But, things like Q5.3 to float can work.  Or, handling a 17 bit enum
in a 32b word.  Or, converting a bool to a single bit.  Or, an
ASCIIZ string to a counted string or fixed size char array.  Or,
a char to a wchar.  UTF16 to UTF8.  "binary" to BCD, etc.

> But it means either using a self describing binary protocol that
> encodes the type and width of each value (and also endianess if
> relevant) so the receiver knows what to do with it.  Or it means
> falling back on a text-based protocol.  Either way it means parsing on
> both sides.

I have a dictionary of data types.  Instead of just picking from
a (relative) few, I let each API decide what is appropriate.
The thinking being that you wouldn't FORCE an application
talking to "itself" to adopt a particular subset of all available
types; so, why force parts of a DECOMPOSED version of that application
to adopt a similar set of constraints?

In my example, a servant only needs to know how to convert
all compatible incoming formats *to* the formats used by the methods
it has exported.  The workload manager won't migrate an object/process
to a node for which converters are unavailable.

E.g., Big Endian SPARC floating point likely won't have a
converter to Little Endian Intel Big Rational.  So, any ABI that
relies on BESPARC will likely be constrained to reside on a SPARC!

Unfortunately, none of the language bindings are sufficiently specific
to automate these type descriptions.  int_t, bcd_t, Q5_3_t, etc.
may all map to the same fundamental type and, thus not gain any
protections from the language.

> https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats
> 
>> The process obviously mirrors for return value(s) conversions.
> 
> In a heterogenous system, the burden of "converting" - whatever that
> entails - usually is placed on the more powerful processor.  Of
> course, ordinarily the 'server' would be the more powerful processor -
> but in your system that's not necessarily the case.
> 
> With self-describing data, each receiver can figure out what to do
> with what it receives.  But that's going to put additional burden on
> low power processors ...

...as well as overtaxed "high power" processors.  If, OTOH, a datum
is just tagged as "format #123" and is already known to be a valid
representation of a #123 datum on the originating client, then
the servant can just blindly invoke the (#123,clientflavor) to
(#123,serverflavor) routine.  The "parsing" is implicit.

Rather than tag each datum IN TRANSIT, I let the message type
reference a dictionary of "payload descriptors".  Those, then,
enumerate the data types and their placement in the payload.

> ... unless you use dedicated comm proxies.  Instead of talking
> directly, communication with a service hosted on a low power system is
> indirected through a proxy that understands and can convert between
> the generic RPC format and the native data format of the service's
> host.

Yes.  The problem, there, is being able to extricate the proxy
when it is no longer needed.  E.g., for Client invoking on Server, you
can interpose a Proxy (possibly residing on Agent).  But, as the
Object backed by the Server may migrate to another (more powerful,
less heavily loaded, etc.) New node, the Proxy must either stay in
the loop and act as a proxy for that migrated objects, UNNECESSARILY;
or, you have to remove the endpoints for that migrated Object
back to the New node (replacing the existing Server endpoint that
had been accessed by the Proxy).

In the degenerate case, you can move all of the objects from
Server to that New node and eliminate the need for the original
Proxy, entirely.  Then, have to eliminate the zombie proxy!

[You can also fashion an "upcall" from the "dumb server" to some
smart agent residing on a better endowed node.  But, this starts
to strain the programming model.]

As the system is far from static, I envision a fair bit of object
migration (which may include server migration) as more processes come
on/off-line.

E.g., I've noticed (in MY coding style) that a process quickly
loses its need for a NameSpace as I tend to resolve any names
early on in the process activation and bind them to more concrete
variabes.  Having no need for the NameSpace, I can "free" it and
thereby lower my resource burden (which makes me look like a
friendlier application to the resource scheduler).

For folks who don't adopt a similar strategy (or, who can't),
a NameSpace object may remain actively bound to their process
for considerably longer.  But, if not regularly accessed,
the NameSpace server on that node may opt to move the NameSpace
object to *another* NameSpace server; the object is still *active*
(and accessible), but the server backing it has decided to
shed those resource requirements to better serve the NameSpaces
that are seeing active use!

Or, the NameSpace server may be remoted by the workload scheduler
to trade off resources between nodes.

An infrequently accessed NameSpace could be backed on a low
performance node with relative ease -- you just need enough
DATA space to store the NameSpace object; access will be
slower but still "correct".

[Of course, *that* node could decide to migrate the NameSpace
object to a BETTER processor if it sees that it is spending
an inordinate amount of resources handling its requests!]

>> [I haven't, yet, considered if anything undesirable "leaks" in either
>> of these approaches]
> 
> What kind of "leaks" are you concerned with?

A process shouldn't be able to determine much about how/where
it is executing *or* its affect on the rest of the system.
The system decides when/where/if you should execute.  And,
can change those decisions without forewarning.  So, anything
that you think you've sussed out NOW can be invalid moments
later.  Or, you may cease to exist those moments later!

Granted, a process could (would!) know the processor family on
which it is executing (by compiling some machine-specific
datum into it's image -- which might even be the command
line used to invoke the compiler).  If it did this frequently,
it *might* be able to deduce that it has been migrated
("Hmmm... I was running on Intel and now I appear to
be running on ARM") -- *if* the migration is to a different
processor family (or build environment).

But, it shouldn't be able to deduce its relative priority,
the demand it may/maynot be placing on its hosting node,
whether an object invocation is actually serviced locally
or remotely, infer the load factor of the node backing that
object, etc.

E.g., passing bad data could leak the "localness" of the targeted
node because it would incur a transport delay before being
"validated" (if the checking was done in the servant).  It could
also leak the types of processes currently active on the
targeted node as its request vs those seen on the "local" node.

Due to the openness of the system, I can't count on processes
being non-malevolent.  Nor can I count on the hardware resources
being adequate for the tasks that they are hosting at any given
time.  I don't want a hostile actor to be able to infer anything
that could be (easily) exploited -- before the system can deduce
that its a hostile actor and kill it (and blackball it so it never
gets loaded, again)!

I'm going to try the client/server split that was suggested in
the original post and throw rocks at it to see how well it fares
in a (artificially) constantly changing configuration.  Then,
try moving the conversions to client and/or servant and see if there
is a measurable difference.

Also, think harder on what each approach might leak and how that might
be exploited.

Back to comp.arch.embedded | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-21 18:38 -0700
  Re: RPC/RMI in heterogeneous environments George Neuner <gneuner2@comcast.net> - 2021-01-25 01:58 -0500
    Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-26 00:06 -0700
      Re: RPC/RMI in heterogeneous environments George Neuner <gneuner2@comcast.net> - 2021-01-26 13:23 -0500
        Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-26 13:39 -0700
          Re: RPC/RMI in heterogeneous environments George Neuner <gneuner2@comcast.net> - 2021-01-28 21:08 -0500
            Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-31 11:44 -0700
              Re: RPC/RMI in heterogeneous environments George Neuner <gneuner2@comcast.net> - 2021-02-01 15:50 -0500
                Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-02-03 21:19 -0700
  Re: RPC/RMI in heterogeneous environments upsidedown@downunder.com - 2021-01-29 20:33 +0200
    Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-31 12:37 -0700
      Re: RPC/RMI in heterogeneous environments upsidedown@downunder.com - 2021-02-01 08:50 +0200
        Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-02-03 20:53 -0700
  Re: RPC/RMI in heterogeneous environments upsidedown@downunder.com - 2021-01-31 21:39 +0200
    Re: RPC/RMI in heterogeneous environments Don Y <blockedofcourse@foo.invalid> - 2021-01-31 15:21 -0700

csiph-web