Skip to content

Thrift columns

Flavian Alexandru edited this page Jun 12, 2016 · 9 revisions

Build Status Coverage Status Maven Central Bintray

Integrating Thrift columns

To use Thrift columns, first and foremost you need a dependency on phantom-thrift. The Thrift module has no explicit dependency on any Apache Thrift library and instead allows Twitter Scrooge, the real underlying serialization mechanism for Apache Thrift, to set the version it needs.

Scrooge is a very powerful integration tool for Scala services built atop Twitter Finagle and Apache Thrift and has the ability to automatically generate entire server and client applications from a simple IDL, and mixing that with a Phantom/Cassandra database store will help you build a very powerful distributed system with minimal effort. Read more about Twitter Scrooge.

libraryDependencies ++= Seq(
  "com.websudos" %% "phantom-thrift" % PhantomVersion
)

These columns are especially useful if you are building Thrift services. They are deeply integrated with Twitter Scrooge and relevant to the Twitter ecosystem(Finagle, Zipkin, Storm etc) They are available via the phantom-thrift module and you need to import the Thrift package to get all necessary types into scope.

import com.websudos.phantom.thrift._

In the below scenario, the Cassandra type is always text and the type you need to pass to the column is a Thrift struct, specifically com.twitter.scrooge .ThriftStruct. phantom will use a CompactThriftSerializer, store the record as a binary string and then reparse it on fetch.

Thrift serialization and de-serialization is extremely fast, so you don't need to worry about speed or performance overhead. You generally use these to store collections(small number of items), not big things.

phantom columns Cassandra columns
ThriftColumn.<type> text
ThriftListColumn.<type> list<text>
ThriftSetColumn.<type> set<text>
ThriftMapColumn.<type, type> map<text, text>

Example

An example is already available in the Thrift test module, but you can review a simple IDL definition here as well.

namespace java com.websudos.phantom.sample.ExampleModel

struct SampleModel {
    1: required string id,
    2: required string name,
    3: optional string description
}

The above model is accompanied by a Cassandra table.

import com.websudos.phantom.dsl._
import com.websudos.phantom.thrift._

// Sample model here comes from the Thrift struct definition.
// The IDL is available in phantom-example/src/main/thrift.
case class SampleRecord(
  stuff: String,
  someList: List[String],
  thriftModel: SampleModel
)

sealed class ThriftTable extends CassandraTable[ThriftTable,  SampleRecord] {
  object id extends UUIDColumn(this) with PartitionKey[UUID]
  object stuff extends StringColumn(this)
  object someList extends ListColumn[ThriftTable, SampleRecord, String](this)


  // As you can see, com.websudos.phantom will use a compact Thrift serializer.
  // And store the records as strings in Cassandra.
  object thriftModel extends ThriftColumn[ThriftTable, SampleRecord, SampleModel](this) {
    def serializer = new CompactThriftSerializer[SampleModel] {
      override def codec = SampleModel
    }
  }

  def fromRow(r: Row): SampleRecord = {
    SampleRecord(stuff(r), someList(r), thriftModel(r))
  }
}

object ThriftTable extends ThriftTable with ExampleConnector

Using Thrift columns as primitives

To use Thrift columns and structs in where clauses and so on, you will need to define an implicit Primitive[ThriftStruct[T]] in scope, where T is the real underlying type you are looking to return. To achieve this, you simply need to create a thrift serializer and use the built in API.

We are going to assume you have a Thrift struct that looks like this, just like in the example we within the code and tests above:

struct ThriftTest {
    1: required i32 id,
    2: required string name,
    3: required bool test
}
import com.youco.models.thrift.ThriftTest

object Example {
   // The first way.
   implicit object thriftTestPritimive extends ThriftPrimitive[ThriftTest] {
    override val serializer = CompactThriftSerializer(ThriftTest)
  }

  // The second way, available as of phantom 1.30.x+
  implicit val thriftPrimitive = {
    ThriftTest.primitive(CompactThriftSerializer(ThriftTest))
  }}
}

Having the above in scope will now allow you to use an instance of a ThriftTest inside a where clause in phantom or any place where indexing is involved.

One example in our codebase available here

 it should "allow storing a thrift class inside a table indexed by a thrift struct" in {
    val sample = gen[Output]

    val chain = for {
      store <- ThriftIndexedTable.store(sample).future()
      // And below you can witness the struct being used as an indexed column.
      get <- ThriftIndexedTable.select.where(_.ref eqs sample.struct).one()
    } yield get

    chain.successful {
      res => {
        res.value.id shouldEqual sample.id
        res.value.name shouldEqual sample.name
        res.value.struct shouldEqual sample.struct
        res.value.optThrift shouldEqual sample.optThrift
        res.value.thriftList shouldEqual sample.thriftList
        res.value.thriftMap shouldEqual sample.thriftMap
        res.value.thriftSet shouldEqual sample.thriftSet

        res.value shouldEqual sample
      }
    }
  }