thrift

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2025 License: Apache-2.0 Imports: 9 Imported by: 0

README

xk6-thrift

A k6 extension for load testing Thrift RPC service, which is light weight and almost native.

[!NOTE] This document is in progress of brushing up

Get started

First of all, prepare your Thrift IDL and run servers implementing it. In the following example, let's assume IDL is like this.

service TestService {
    string simpleCall(1: string id);
}

Then, create your load testing file with JavaScript.

import thrift from 'k6/x/thrift';
import ttypes from 'k6/x/thrift/ttypes';

export default function() {
  const method = "simpleCall";
  // prepare arguments as map.
  const values = {};
  // "1" means ID of the argument.
  values[1] = ttypes.newTString("ID");
  // wrap map of request arguments.
  const req = ttypes.newTRequest(values);

  thrift.call(method, req);
}

What you have to configure are...

  • Specify Thrift service method name.
    • simpleCall in the above examlpe.
  • Build request body with ttypes.newTXxx() method.

Now it's ready to call Thrift RPC service.

See scripts/ directory for more examples.

Install

Install binaries

Currently preparing binary. Follow the procedure mentioned in the next section. (:sorry:)

Install by building source
with Golang

Followings are the requirements.

  • Golang v1.23 or higher (tested in this version)

First, install xk6 go library.

$ go install go.k6.io/xk6/cmd/xk6@latest
# you can find xk6 command is installed
$ xk6 version

Then (and finally), build binary using xk6-thrift extension with following command. Use the xk6 version you want to use instead of v0.57.0.

Currently v0.1.0 is the latest version of xk6-thrift. Specify any version you want to use.

$ xk6 build v0.57.0 \
  --with github.com/lavenderses/xk6-thrift@v0.1.0

For more information, please refer k6s document: Build a k6 binary using Go | Grafana k6 documentation

with Docker

Alternatively, you can build binary using Docker when you don't have Docker envrionment. If you want to run built binary on other architecture platforms such as windows, specify GOOS env var.

$ git clone git@github.com:lavenderses/xk6-thrift && cd xk6-thrift
$ git checkout x0.1.0 # Any revision you want to use
$ docker run --rm -u "$(id -u):$(id -g)" -v "${PWD}:/xk6" grafana/xk6 build \
  --with github.com/mostafa/xk6-thrift=.

For more information, please refer k6s document: Build a k6 binary using Docker | Grafana k6 documentation

Features

These are currently supported.

  • Thrift using HTTP as transport layer.
  • Thrift types
    • string (Use ttypes.newTString())
    • boolean (Use ttypes.newTBool())
    • map (Use ttypes.newTMap())
    • struct (Use ttypes.newTStruct())
Planning features
  • Thrift using TCP as transport layer.
  • Thrift types
    • numbers such as int16, int32 etc
    • list
    • set
    • uuid
  • schema referencing by JSON generated by Thrift compiler
  • check Thrift response mechanism

Detailed usage

[!NOTE] Type in JavaScript is called ttypes in this document.

Type mapping between Thrift and JavaScript
string (ttypes string)

Thrift string is mapped to string in JavaScript.

You can use ttypes string by ttypes.newTString(string) function.

boolean

Thrift boolean is mapped to boolean in JavaScript. Similar to string.

You can use ttypes boolean *by ttypes.newTBool(boolean) function.

map

Thrift map is maped to dictionary in JavaScript.

Key and value in the dictionary should be ttypes. For example, map can be defined like this.

struct Foo {
  1:map<string, bool> bar,
}
import ttypes from 'k6/x/thrift/thrift/ttypes';

// define raw value as dictionary
const rawMap = {}
// define key and value as ttypes
rowMap[ttypes.newTString("key 1")] = ttypes.newTBool(true);
rowMap[ttypes.newTString("key 2")] = ttypes.newTBool(false);
// wrap the raw value dictionary with ttypes.newTMap()
const bar = ttypes.newTMap(bar);
struct

Similar to map, Thrift struct is mapped to dictionary in JavaScript.

But, unlike map, key of the dictionary should be number, not ttypes. And the key must be a Thrift field ID. Value is ttypes.

For example, struct can be defined like this.

struct Foo {
  1:string bar,
  2:map<string, bool> buz,
}
import ttypes from 'k6/x/thrift/thrift/ttypes';

// define string and map<string, bool> like the above example.
const bar = ttypes.newTString(...);
const buz = ttypes.newTMap( {...} );

// define raw value as dictionary
const rawStruct = {}
// define key and value as ttypes. key should be a field ID.
rawStruct[1] = bar;
rawStruct[2] = buz
// wrap the raw value dictionary with ttypes.newTStruct()
const foo = ttypes.newTStruct(rawStruct);
Calling RPC service

To call Thrift RPC service, you have to create request body class. Use ttypes.newTRequest(dictionary<int, ttypes>) function in 'k6/x/thrift/ttypes' module.

Arguments of ttypes.newTRequest(), which is dictionary, correspond to arguments in the target Thrift RPC.

Key of the dictionary is the number of ID in Thrift definition. And value of it is a instance created by ttypes.newTXxx() method. For information about supported types and functions to create them, please read following Types in JavaScript section.

e.g.

struct FooRequest {
  1:string foo,
}

struct BarRequest {
  1:bool foo,
}

service YourService {
  string methodCall(1: FooRequest fooRequest, 2:string something, 3:BarRequest barRequest);
}
import thrift from 'k6/thrift/thrift';
import ttypes from 'k6/thrift/thirft/ttypes';

// define struct like the above section.
const fooRequest = ttypes.newTStruct( {...} );
const something = ttypes.newTString("...");
const barRequest = ttypes.newTStruct( {...} );

// define raw value as dictionary
const rawRequest = {}
// define key and value as ttypes. key should be an ID of argument
rawRequest[1] = fooRequest
rawRequest[2] = something
rawRequest[3] = barRequest
// wrap the raw value dictionary with ttypes.newTRequest()
const request = ttypes.newTRequest(rawRequest)
// call method with method name and its request
const metthodName = "methodCall"
thrift.call(methodName, request)

Development

How to use in local

Server implementation is included in this repository. See idl/ directory for Thrift IDL and server/ directory for server implementation. Only thing you have to use is installing Java 17 or higher and run server on it. SDKMAIN! is the recommended way to install Java.

After that, do the following command on root directory of this repository. Thrift server will be started on port 8080, path /thrift.

$ ./server/gradlew run -p server/server/
# check server is running on another terminal
$ curl -v http://localhost:8080/health

Or you can use any server implementation you want to use. Please compile Thrift IDL and run server.

LICENSE

Apache License 2.0. See LICENSE.

Contribution

Any issue report, feedback, feature request or pull request is welcome. Please feel free to open an issue or submit pull request.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type TBool

type TBool struct {
	// contains filtered or unexported fields
}

func NewTBool

func NewTBool(v bool) TBool

func (TBool) Equals

func (p TBool) Equals(other *TValue) bool

func (TBool) WriteField

func (p TBool) WriteField(cxt context.Context, oprot thrift.TProtocol, fid int16, fname string) (err error)

See Thrift IDL protocol spec

<field> ::= <field-begin> <field-data> <field-end>
<field-data> ::= BOOL

type TMap

type TMap struct {
	// contains filtered or unexported fields
}

func NewTMap

func NewTMap(v *map[TValue]TValue) *TMap

func (*TMap) Equals

func (p *TMap) Equals(other *TValue) bool

func (*TMap) WriteField

func (p *TMap) WriteField(cxt context.Context, oprot thrift.TProtocol, fid int16, fname string) (err error)

See Thrift IDL protocol spec

<field>      ::= <field-begin> <map> <field-end>
<map>        ::= <map-begin> <field-data>* <map-end>
<field-data> ::= I8 | I16 | I32 | I64 | DOUBLE | STRING | BINARY
                 <struct> | <map> | <list> | <set>

type TModule

type TModule struct{}

func (*TModule) Call

func (m *TModule) Call(method string, req *TRequest)

func (*TModule) Echo

func (m *TModule) Echo()

type TRequest

type TRequest struct {
	// contains filtered or unexported fields
}

func NewTRequest

func NewTRequest() *TRequest

func NewTRequestWithValue

func NewTRequestWithValue(v *map[int16]TValue) *TRequest

func (*TRequest) Read

func (p *TRequest) Read(cxt context.Context, iprot thrift.TProtocol) (err error)

func (*TRequest) Write

func (p *TRequest) Write(cxt context.Context, oprot thrift.TProtocol) (err error)

type TResponse

type TResponse struct {
	// contains filtered or unexported fields
}

func NewTResponse

func NewTResponse() *TResponse

func (*TResponse) Add

func (p *TResponse) Add(key int16, value TValue)

func (*TResponse) Read

func (p *TResponse) Read(cxt context.Context, iprot thrift.TProtocol) error

func (*TResponse) ReadBool

func (p *TResponse) ReadBool(cxt context.Context, iproto thrift.TProtocol, fieldId int16) (*TBool, error)

func (*TResponse) ReadMap

func (p *TResponse) ReadMap(cxt context.Context, iproto thrift.TProtocol, fieldId int16) (*TMap, error)

func (*TResponse) ReadString

func (p *TResponse) ReadString(cxt context.Context, iproto thrift.TProtocol, fieldId int16) (*TString, error)

func (*TResponse) ReadStruct

func (p *TResponse) ReadStruct(cxt context.Context, iprot thrift.TProtocol, fieldId int16) (*TStruct, error)

func (TResponse) Values

func (p TResponse) Values() *map[int16]TValue

func (*TResponse) Write

func (p *TResponse) Write(cxt context.Context, oprot thrift.TProtocol) error

dummy.

type TString

type TString struct {
	// contains filtered or unexported fields
}

func NewTstring

func NewTstring(v string) TString

func (TString) Equals

func (p TString) Equals(other *TValue) bool

func (TString) WriteField

func (p TString) WriteField(cxt context.Context, oprot thrift.TProtocol, fid int16, fname string) (err error)

See Thrift IDL protocol spec

<field> ::= <field-begin> <field-data> <field-end>
<field-data> ::= STRING

type TStruct

type TStruct struct {
	// contains filtered or unexported fields
}

func NewTStruct

func NewTStruct(value *map[TStructField]TValue) *TStruct

func (*TStruct) Equals

func (p *TStruct) Equals(other *TValue) bool

func (*TStruct) WriteField

func (p *TStruct) WriteField(cxt context.Context, oprot thrift.TProtocol, fid int16, fname string) (err error)

see Thrift protocol spec @ 1a31d90 (v0.21.0).

<field> ::= <field-begin> <field-data> <field-end>
	<field-data> ::= I8 | I16 | I32 | I64 | DOUBLE | STRING | BINARY
<struct> | <map> | <list> | <set>
<struct> ::= <struct-begin> <field>* <field-stop> <struct-end>

type TStructField

type TStructField struct {
	// contains filtered or unexported fields
}

func NewTStructField

func NewTStructField(id int16, name string) *TStructField

type TTypes

type TTypes struct{}

func (*TTypes) NewTRequest

func (*TTypes) NewTRequest(v *map[int16]TValue) *TRequest

func (*TTypes) NewTString

func (*TTypes) NewTString(v string) TString

type TValue

type TValue interface {
	Equals(other *TValue) bool
	// WriteField outputs TValue to Thrift protocol with field ID `fid` and field name `fname`.
	// This must start with `WriteFieldBegin` method call, and end with `WriteFieldEnd` method call.
	WriteField(cxt context.Context, oprot thrift.TProtocol, fid int16, fname string) error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL