Handling nested contexts on the server side

43 views
Skip to first unread message

Bahri Gençsoy

unread,
Feb 6, 2020, 3:11:41 AM2/6/20
to grpc.io

Hello,

This design question is not explicitly related to GRPC but applicable to any message based communication system, say REST or WSDL.

I want to maintain nested contexts on the server side. I use the term "context" in a general way, not necessarily mean the "Grpc Context Helper Class".

In my design, each call can create a new context, and the upcoming calls will use the parent context. See the example proto:

syntax = "proto3";

import "google/protobuf/empty.proto";

package com.example.threatservice;

service ThreatService {
rpc registerClient (ClientRegistrationRequest) returns (ClientRegistrationResponse);
rpc subscribeClientCallback (ClientSubscriptionRequest) returns (stream ThreatEvent);
rpc initiateThreat(ThreatInitiationRequest) returns (ThreatInitiationResponse);
rpc associateIpAddressWithThreat(ThreatIpAssociationRequest) returns (google.protobuf.Empty);
rpc publishThreat(ThreatPublishRequest) returns (google.protobuf.Empty);
}

message ClientRegistrationRequest {
string clientId = 1;
}

message ClientRegistrationResponse {
string registrationContextId = 1;
}

message ClientSubscriptionRequest {
string clientId = 1;
string registrationContextId = 2;
}

message ThreatEvent {
string eventContextId = 1;
uint64 threatStartDate = 2;
}

message ThreatInitiationRequest {
string clientId = 1;
string registrationContextId = 2;
string eventContextId = 3;
}

message ThreatInitiationResponse {
string threatContextId = 1;
}

message ThreatIpAssociationRequest {
string clientId = 1;
string registrationContextId = 2;
string eventContextId = 3;
string threatContextId = 4;
string ipAddress = 10;
}

message ThreatPublishRequest {
string clientId = 1;
string registrationContextId = 2;
string eventContextId = 3;
string threatContextId = 4;
}

The ThreatService holds the registration of clients, and asks them for new threats from time to time.

Client is responsible to pass the parent context id while creating a new context. Since we know the graph on the server side it is not obligatory for the client to track all the parent contexts to the root. So the client requests can be simplified to:

message ClientRegistrationRequest {
string clientId = 1;
}

message ClientSubscriptionRequest {
string registrationContextId = 2;
}

message ThreatInitiationRequest {
string eventContextId = 3;
}

message ThreatInitiationResponse {
string threatContextId = 1;
}

message ThreatIpAssociationRequest {
string threatContextId = 4;
string ipAddress = 10;
}

message ThreatPublishRequest {
string threatContextId = 4;
}

So, writing a client is not that difficult. Also writing a server that would only handle happy paths.

But when the rainy path occurs, it is easy to create a mess on the server side. In the following scenario:

* Client registers itself   -> Server creates record for the registration in memory
* Client subscribe to events  -> Server creates a record for the subscription in memory
* Client initiates a new threat  -> Server creates a record for the threat in memory
* Client associates an ip address to threat  ->  Server creates a record for the ip address in memory
* Client associates another ip address to the threat  ->  Server creates a record for the ip address in memory
* Client fails and connection drops

So, on the server side we need the get rid of the all the garbage.

It would have been so easy if the client was a synchronous, in-process Java implementation; the garbage collector would get rid of the all the data.

I need a systematic approach to solve the problem. It would probably use the Context class, and some tree structure on the server side, but I don't want to reinvent the wheel or try to solve the incorrect problem. It might be just better not to create nested context for this example, say the ThreatPublishRequest can hold all the ip addresses inside the message, but I suspect I won't be able to avoid contexts all together.


Any ideas?
Thanks

Eric Anderson

unread,
Feb 10, 2020, 7:28:39 PM2/10/20
to Bahri Gençsoy, grpc.io
If you want to store temporary state just as long as a client is around, you can use a stream. For example, transactions and locks and use a gRPC streaming call to good success, because if the client crashes the stream will (eventually) fail.

Although it is a bit hard to tell in your case how long this information needs to be kept around. If it is "minutes," one problem may be load balancing. Long-lived RPCs need to be rebalanced occasionally and that rebalancing requires re-creating the stream, which wouldn't have any of the previous data. Another issue is how you would want to handle transient connectivity issues between the client and server (e.g., if the stream gets killed but both the client and server are still running, and new RPCs still work).

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/05b7f576-54ef-4de8-9e17-8d622fcd3299%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages