Search code examples
gothrift

How do I make a thrift call to a specific service of a server that supports multiple versions


Let's say I have the following services declared on the same thrift file: [I will omit the struct definition since this is merely an example]

 service PositionService {
   PositionDTO findPosition(1:positionInputDTO) throws (1:PositionServiceException e);
 }

 service PositionServiceV2 {
   PositionDTO findPosition(1:positionInputDTO) throws (1:PositionServiceException e),
   OwnerStoresListDTO listPositions(1:ListPositionInputDTO) throws (1:PositionServiceException e);
 }

I compiled this thrift file and generated code for the server in java and go code for the client. Then I tried to call listPositions method on the server using PositionServiceV2Client but I kept getting the error Invalid method name: listPositions as a response from the server.

Keep in mind that both the client and server are using a compatible stack for these calls, I already implemented other services but this is the first one I ever come across that has multiple services declared inside the same thrift file. How can I make such a call happen? Any help is appreciated.


Solution

  • Calling newer services "the old fashioned way"

    Calling an PositionService server using a PositionServiceV2 client is not possible, because it is a different service. And even if you get it to succeed, you will still run into troubles when you start using service multiplexing.

    Even if the method names accidentally match like with findPosition(), there is no guarantee that the call succeeds. The argument list may be different, there may be different return data or the throws clause may not match. From the interface contract, these are two different services.

    Bottom line: You can call a PositionServiceV2 service using a PositionServiceV2 client, or a PositionService server using an PositionService client.

    But in that case you still have an option left. Thrift supports inheritance at the service level, like so:

    service PositionService {
        PositionDTO findPosition(1:PositionInputDTO) 
            throws (1: PositionServiceException e);
    }
    
    service PositionServiceV2 extends PositionService {
        OwnerStoresListDTO listPositions(1:ListPositionInputDTO)  
            throws (1:PositionServiceException e);
    }
    

    Of course, a server can still only respond to calls that are actually implemented server-side. Targeting a V1 server using a V2 client will not magically implement the V2 interface.

    What is "soft versioning" anyway?

    With Thrift you don't need to do all of that. You can basically forget what we wrote above and do it even simpler:

    service PositionService {
    
        // this method exists from the beginning
        PositionDTO findPosition(1:PositionInputDTO) 
            throws (1: PositionServiceException e);
    
        // this method has been added later
        OwnerStoresListDTO listPositions(1:ListPositionInputDTO)  
            throws (1:PositionServiceException e);
    
        // this method is no longer available
        //void SomeOlderCallThatIsNoLongerSupportedAnymore()
    }
    

    This is called "soft versioning" because it completely eliminates the need of distinguishing betwen, calling, implementing and maintaining different "versions" of the same API. The call is either there or not.

    This concept would not be possible with binary APIs like COM interfaces, because changing an existing API breaks the contract immediately. But it is possible using Thrift and similar frameworks. You still can introduce incompatibe changes if you try hard enough, but you can still call the remainders.

    If you really need to get some information about what a server is capable of, what is supported, consider introducing some helper method like so:

    enum ServerCaps = { CanFoo, CanBar, SupportsBaz }
    
    service PositionService {
        // tell me what features you support
        set<ServerCaps>  GetServerCapabilities()
    
        //... other methods here ...
    }
    

    PS: Asked and answered before