Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Tinkerpop with actors

I would like to know if I can use tinkerpop within Akka Futures, so far when I commit my changes to the graph they don't get persisted. I understand tinkerpop is a thread-local library which mean I'd need to set my thread again within the future ODatabaseRecordThreadLocal.INSTANCE.set(thread)

I tried the following method without success :

def test[T](graphChanges: => T): T = {
    val thread = ODatabaseRecordThreadLocal.INSTANCE.get
    try graphChanges finally {
      ODatabaseRecordThreadLocal.INSTANCE.set(thread)
      GraphPool.get("partitioned").commit
    }
}

// collect tinkerpop frames
test {
  future {
  // add changes to my tinkerpop frames
  }
}

I would like to have on Tinkerpop thread per play.mvc.Http.Context

Here is a sample project with what I want to achieve : https://github.com/D-Roch/tinkerpop-play

like image 981
Roch Avatar asked Jan 02 '14 15:01

Roch


People also ask

What is TinkerPop?

Why TinkerPop? The goal of TinkerPop, as a Graph Computing Framework, is to make it easy for developers to create graph applications by providing APIs and tools that simplify their endeavors.

Is TinkerPop an abstraction over spark?

TinkerPop has always had the vision of being an abstraction over different graph databases. That much is not new and dates back to TinkerPop 1.x. It is in TinkerPop 3.x, however, that we see the introduction of the notion that TinkerPop is also an abstraction over different graph processors like Spark.

Can you build graph applications with TinkerPop?

Gremlin, the most recognizable citizen of The TinkerPop, is here to help with this thirty-minute tutorial. That’s right: in just thirty short minutes, you too can be fit to start building graph applications with TinkerPop. Welcome to The TinkerPop Workout — by Gremlin!

What's new in TinkerPop 3?

This means that all documentation is now integrated with Gremlin, and traversal steps are now found on the official TinkerPop site. Gremlin-Groovy is the default flavor in TinkerPop3. This is different to the Java getters/setters approach of TinkerPop2.


2 Answers

The problem

The problem is, that Tinkerpop works thread local. So your changes are only committed to the current thread. When creating Scala futures, you let the environment choose, in which thread the future will be executed. And the environment doesn't know better, so it chooses the wrong thread.

The problem is similar for Akka futures.

In which thread does a future run?

When creating a future, you are creating it with two parameters:

  1. The block that should be executed
  2. The Execution Context that should execute the block

The second parameter is usually given as an implicit parameter. But you can override the default.

Solution

When creating futures dealing with Tinkerpop, use an execution context that runs every block in the same thread.

Example:

import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors

implicit val ec=ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor)

future { 
    println(Thread.currentThread); 
    future {
        println(Thread.currentThread)
    }  
}

This code prints out the same thread id twice on the console (tested with Java 7 and Scala 2.10.2.

Attention: Using such a small thread pool can easily lead to dead locks or starvation. Use it only for your Tinkerpop interaction.

You can either provide a special method tinkerpopFuture that takes a block as an parameter and returns a future that will run in the tinkerpop thread. Or you can create a special actor which encapsulates all tinkerpop interactions (and runs them with the special tinkerpop exection context).

Literature

  • You can see some example code for creating an execution context here.
  • Documentation for Akka Futures and execution contexts
like image 73
stefan.schwetschke Avatar answered Oct 16 '22 03:10

stefan.schwetschke


This doesn't look like anything specific to Tinkerpop, it looks like a common error made with using Futures. Just consider this fragment:

try graphChanges finally { ... }

It looks fine by itself, but I can also see that graphChanges here is creating a future. So...

  • graphChanges initiates a Future, returning instantly
  • the try block completes and the finally block is executed
  • At some point immediately before this, or after, or maybe in parallel, but almost certainly on another thread, the Future is executed

My advice would be to move the asynchronous logic inside test, so that you can be sure of the correct thread-affinity and ensure that any calls are correctly flagged as blocking. Like this:

def test[T](graphChanges: => T): Future[T] = future {
  blocking {
    val tlocal = ODatabaseRecordThreadLocal.INSTANCE
    val dbrecord = tlocal.get

    try graphChanges finally {
      tlocal.set(dbrecord)
      GraphPool.get("partitioned").commit
    }
  }
}

// collect tinkerpop frames
test {
  // add changes to my tinkerpop frames
}
like image 20
Kevin Wright Avatar answered Oct 16 '22 04:10

Kevin Wright