8. Who?
Lead platform developer at Burt
Ad analytics startup
Real time analytics processing
~15k requests/s
JRuby 1.6.x, RabbitMQ, MongoDB, Redis,
Cassandra
28. Array.join
Joining an array of 1 000 entries, 10 000 times
strings 0.771s
symbols 3.171s +2.4s
int 6.588s +5.817s
29. Array.join
Joining an array of 1 000 entries, 10 000 times
strings 0.771s
symbols 3.171s +2.4s
int 6.588s +5.817s
class Symbol/Numeric/NilClass
alias_method :to_str, :to_s
end
30. Array.join
Joining an array of 1 000 entries, 10 000 times
strings 0.771s
symbols 3.171s +2.4s
int 6.588s +5.817s
class Symbol/Numeric/NilClass
alias_method :to_str, :to_s
end
strings 0.771s
symbols 1.781s +1.01s (-1.39s)
int 5.484s +4.713s (-1.104s)
41. Queues
Internal FIFO queue using an array
Putting data with shift/push or pop/unshift
Alternatives:
Fixed size array with sweeping index
42. Queues
Internal FIFO queue using an array
Putting data with shift/push or pop/unshift
Alternatives:
Fixed size array with sweeping index
LinkedList
43. Queues
Internal FIFO queue using an array
Putting data with shift/push or pop/unshift
Alternatives:
Fixed size array with sweeping index
LinkedList
LinkedBlockingQueue
48. Queues
queue = Queue.new(size)
100_000.times do |i|
data = queue.pop
queue.push(i)
end
Queue(1_000)
array 0.065s
index 0.030s
LinkedList 0.056s
Queue(100_000)
array 3.996s 85 times slower
index 0.047s
LinkedList 0.137s
49. Queues
queue = Queue.new(size)
100_000.times do |i|
data = queue.pop
queue.push(i)
end
Queue(1_000)
array 0.065s
index 0.030s
LinkedList 0.056s
Queue(100_000)
array 3.996s 85 times slower
index 0.047s
LinkedList 0.137s
Queue(1_000_000)
array 53.895s 216 times slower!
index 0.249s
LinkedList 0.847s
53. Concurrency
Use a thread pool (ie
java_import java.util.concurrent.Executors
executor = Executors.newFixedThreadPool(2)
10.times do
executor.submit do
# do stuff
end
end
54. Concurrency
Use a thread pool (ie
java.util.concurrent.Executors)
Ruby’s Mutex? You’re doing it wrong
55. Concurrency
Use a thread pool (ie
java.util.concurrent.Executors)
Ruby’s Mutex? You’re doing it wrong
java.util.concurrent is your friend
56. Concurrency
Use a thread pool (ie
java.util.concurrent.Executors)
Ruby’s Mutex? You’re doing it wrong
java.util.concurrent is your friend
AtomicInteger, ConcurrentHashMap,
LinkedBlockingQueue
57. Concurrency
Use a thread pool (ie
java.util.concurrent.Executors)
Ruby’s Mutex? You’re doing it wrong
java.util.concurrent is your friend
AtomicInteger, ConcurrentHashMap,
LinkedBlockingQueue
Lock, Semaphore, CountdownLatch - IF you have to!
60. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
61. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
62. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
63. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
There’s almost always a way of doing it without shelling
64. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
There’s almost always a way of doing it without shelling
Don’t pass binary RubyString to Java
65. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
There’s almost always a way of doing it without shelling
Don’t pass binary RubyString to Java
Example: Hot_bunnies (JRuby RabbitMQ gem)
66. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
There’s almost always a way of doing it without shelling
Don’t pass binary RubyString to Java
Example: Hot_bunnies (JRuby RabbitMQ gem)
Serialized data -> Ruby -> Java -> Network -> Java ->
Ruby
67. Other stuff to keep in mind
Shelling out is significantly slower in JRuby
`mkdir -p /some/path/deep/1`
rake, capistrano, bundle exec
There’s almost always a way of doing it without shelling
Don’t pass binary RubyString to Java
Example: Hot_bunnies (JRuby RabbitMQ gem)
Serialized data -> Ruby -> Java -> Network -> Java ->
Ruby
String#to_java_bytes / String.from_java_bytes
72. Conclusion
Beware of unnecessary string creation
Watch out for array.join with non-string objects
Don’t shift/unshift large arrays
73. Conclusion
Beware of unnecessary string creation
Watch out for array.join with non-string objects
Don’t shift/unshift large arrays
java.util.concurrent is your friend!
74. Conclusion
Beware of unnecessary string creation
Watch out for array.join with non-string objects
Don’t shift/unshift large arrays
java.util.concurrent is your friend!
Don’t shell if you don’t have to
75. Conclusion
Beware of unnecessary string creation
Watch out for array.join with non-string objects
Don’t shift/unshift large arrays
java.util.concurrent is your friend!
Don’t shell if you don’t have to
Remember String#to_java_bytes
Quick show of hands\nWho here runs a ruby system in production?\nWhat about jruby?\nWho runs an advanced jruby system, as in using java modules, threading, java specific gems, that sort of thing?\n
I’m David, and i’m the lead platform developer at Burt\nThat means i’m scrum master and i run the day to day development of the platform team\n\nBurt is an ad analytics startup. Simply put we track ads on your website, crunch a whole lot of data about it and present it in nice reports.\nThe platform team that i’m in runs the whole crunching pipeline, and we process about 15k requests per second at peak times, in real time. \nWe use various versions of JRuby 1.6 in Ruby 1.9 mode, along with tools like RabbitMQ, MongoDB, Redis and Cassandra.\n\n
I’m David, and i’m the lead platform developer at Burt\nThat means i’m scrum master and i run the day to day development of the platform team\n\nBurt is an ad analytics startup. Simply put we track ads on your website, crunch a whole lot of data about it and present it in nice reports.\nThe platform team that i’m in runs the whole crunching pipeline, and we process about 15k requests per second at peak times, in real time. \nWe use various versions of JRuby 1.6 in Ruby 1.9 mode, along with tools like RabbitMQ, MongoDB, Redis and Cassandra.\n\n
I’m David, and i’m the lead platform developer at Burt\nThat means i’m scrum master and i run the day to day development of the platform team\n\nBurt is an ad analytics startup. Simply put we track ads on your website, crunch a whole lot of data about it and present it in nice reports.\nThe platform team that i’m in runs the whole crunching pipeline, and we process about 15k requests per second at peak times, in real time. \nWe use various versions of JRuby 1.6 in Ruby 1.9 mode, along with tools like RabbitMQ, MongoDB, Redis and Cassandra.\n\n
I’m David, and i’m the lead platform developer at Burt\nThat means i’m scrum master and i run the day to day development of the platform team\n\nBurt is an ad analytics startup. Simply put we track ads on your website, crunch a whole lot of data about it and present it in nice reports.\nThe platform team that i’m in runs the whole crunching pipeline, and we process about 15k requests per second at peak times, in real time. \nWe use various versions of JRuby 1.6 in Ruby 1.9 mode, along with tools like RabbitMQ, MongoDB, Redis and Cassandra.\n\n
I’m David, and i’m the lead platform developer at Burt\nThat means i’m scrum master and i run the day to day development of the platform team\n\nBurt is an ad analytics startup. Simply put we track ads on your website, crunch a whole lot of data about it and present it in nice reports.\nThe platform team that i’m in runs the whole crunching pipeline, and we process about 15k requests per second at peak times, in real time. \nWe use various versions of JRuby 1.6 in Ruby 1.9 mode, along with tools like RabbitMQ, MongoDB, Redis and Cassandra.\n\n
What i’m going to talk about are some of the things we’ve learned when building and optimizing our system. \nOur use case is very specific, and not everything i’m going to talk about applies to everyone.\nIt’s a very different thing writing a long running application as opposed to a web application where every session is very short lived. \n\n
What i’m going to talk about are some of the things we’ve learned when building and optimizing our system. \nOur use case is very specific, and not everything i’m going to talk about applies to everyone.\nIt’s a very different thing writing a long running application as opposed to a web application where every session is very short lived. \n\n
What i’m going to talk about are some of the things we’ve learned when building and optimizing our system. \nOur use case is very specific, and not everything i’m going to talk about applies to everyone.\nIt’s a very different thing writing a long running application as opposed to a web application where every session is very short lived. \n\n
What i’m going to talk about are some of the things we’ve learned when building and optimizing our system. \nOur use case is very specific, and not everything i’m going to talk about applies to everyone.\nIt’s a very different thing writing a long running application as opposed to a web application where every session is very short lived. \n\n
What i’m going to talk about are some of the things we’ve learned when building and optimizing our system. \nOur use case is very specific, and not everything i’m going to talk about applies to everyone.\nIt’s a very different thing writing a long running application as opposed to a web application where every session is very short lived. \n\n
First thing i want to talk about are strings.\nIt’s something we all use, all the time\nBut there’s one big pitfall\n\n
Writing a string literal in Ruby creates a new string.\nWhich means that this (“”), is the same as this (String.new)\nMost of the time, this isn’t a problem, but if you do something like this that could be trouble\n\nThe problem here is that when looking at this code, you get a feeling that string_key is the same string all the time. But it’s not, there’s a new string created on every iteration of this loop. This is because strings in ruby are mutable, as compared to languages like Java, C# or Python.\n
Writing a string literal in Ruby creates a new string.\nWhich means that this (“”), is the same as this (String.new)\nMost of the time, this isn’t a problem, but if you do something like this that could be trouble\n\nThe problem here is that when looking at this code, you get a feeling that string_key is the same string all the time. But it’s not, there’s a new string created on every iteration of this loop. This is because strings in ruby are mutable, as compared to languages like Java, C# or Python.\n
Writing a string literal in Ruby creates a new string.\nWhich means that this (“”), is the same as this (String.new)\nMost of the time, this isn’t a problem, but if you do something like this that could be trouble\n\nThe problem here is that when looking at this code, you get a feeling that string_key is the same string all the time. But it’s not, there’s a new string created on every iteration of this loop. This is because strings in ruby are mutable, as compared to languages like Java, C# or Python.\n
What you want to do instead is putting that string in a constant, and then use the constant when working with the hash. Or even better, use a symbol or an int.\n\nHere are some benchmarks for doing this a million times with different keys.\nAs you can see, the string version is slowest, almost twice as slow as using ints as keys\n\nObviously, some of this time goes to calculating the hash codes, which is why int is so much faster than the others.\n\n
Now, the really interesting difference becomes apparent if we turn on verbose garbage collection\n\nThis is the printout for a minute run of a similar loop using a string key\nAs you can see, the GC has to work quite a lot.\nIn comparison, there is NO GC activity at all when using a constant or a symbol\n\n
This is what the memory use looks like.\nNow, in a simple loop the effect of this is quite small. The GC doesn’t really have any problems dealing with it. But once you put this in a complex application along with several other threads and other stuff, and then let it run for a couple of days, this can cause a significant slowdown.\n
As a comparison, the upper graph shows what the memory use looks like with a constant. Do note that the upper limit for that instance is 25 mb, compared to the string example that fluctuates between 25 and 275 mb.\n\nBtw, these graphs are from VisualVM, which is an excellent tool for looking into performance and memory issues in java, and it works great for Jruby too\n
\n
The next thing is something we discovered recently.\nWe had a spot where we took a bunch of different variables, stuck them in an array and joined them together into a coordinate like this\n\nWhen we profiled the code we saw a LOT of calls to method_missing which didn’t make any sense, and it was a real hot spot, like 15-20% of the processing time\n\nTurns out, Array.join uses to_str, not to_s. Which when you think about it makes a lot of sense.\nto_str is an implicit cast, or a more strict conversion. You should only implement it if your object can naturally be used every place a string could be used.\nSo, to_str is not implemented on symbols, fixnum or nilclass.\n\nBut, ruby is clever! So when to_str doesn’t exist, it goes down to method missing and eventually finds it way to to_s. But as you can see, thats quite a call stack being generated. So there’s a lot of overhead.\n
The next thing is something we discovered recently.\nWe had a spot where we took a bunch of different variables, stuck them in an array and joined them together into a coordinate like this\n\nWhen we profiled the code we saw a LOT of calls to method_missing which didn’t make any sense, and it was a real hot spot, like 15-20% of the processing time\n\nTurns out, Array.join uses to_str, not to_s. Which when you think about it makes a lot of sense.\nto_str is an implicit cast, or a more strict conversion. You should only implement it if your object can naturally be used every place a string could be used.\nSo, to_str is not implemented on symbols, fixnum or nilclass.\n\nBut, ruby is clever! So when to_str doesn’t exist, it goes down to method missing and eventually finds it way to to_s. But as you can see, thats quite a call stack being generated. So there’s a lot of overhead.\n
This is a benchmark of joining an array of 1000 entries ten thousand times.\nAs you can see there’s quite a performance hit\n\nOne thing you can do to combat this is to map to_str to to_s. As you can see the overhead is lower, but to_s is still slow.\nBUT, this is not a good idea. Why? \n\n
This is a benchmark of joining an array of 1000 entries ten thousand times.\nAs you can see there’s quite a performance hit\n\nOne thing you can do to combat this is to map to_str to to_s. As you can see the overhead is lower, but to_s is still slow.\nBUT, this is not a good idea. Why? \n\n
Struct is the reason. If you’re not familiar with Struct, it’s a way of bundling attributes together without creating a full class.\nStruct has an optional string argument that names the class. And this is detected with duck-typing by checking to_str.\n\nSo if you send in an object responding to to_str as the first argument, Struct will demand that it behaves like a constant and starts with a capital letter. So if you created an anonymous structure with a symbol, and then added to_str to symbol, you could guess what happens.\n\nAnd the thing is, there’s a lot of gems using anonymous structs. Like webbrick. So if you have this to_str hack, and then decide to require webbrick, everything will crash.\n\n\n\n
So what can we do instead? A simple solution is to convert every item in the array to a string explicitly before joining.\nIt’s not quite as fast as implementing alias_method, but it’s non-invasive and improves performance.\n
So what can we do instead? A simple solution is to convert every item in the array to a string explicitly before joining.\nIt’s not quite as fast as implementing alias_method, but it’s non-invasive and improves performance.\n
\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
My next example comes from troubles we had with an internal FIFO queue.\nWhat we discovered was that when the internal buffer grew, we saw diminishing performance up to a point where it was impossible to catch up.\n\nWhat happens is that shift and unshift on an array causes the entire array to be moved in memory, and that’s quite costly if there’s a lot of entries.\n\nThere are a couple of ways to do this better. You could use a fixed size array with a sweeping index.\nOr you could go to Java and used a LinkedList. If you’re using threads, this is the way you want to go in the first place. Go straight for a LinkedBlockingQueue.\n
This is an example of a sweeping index array queue. You have a fixed size array and two indexes. And data between those indexes.\n
Then you pop a few items from the queue, and the start index is increased.\n
And if you push items to the array and you run over the edge, you start from the beginning again.\n
As you can see, for small queues the difference is minimal\nBut here’s what happens when we grow the queue\n\n
As you can see, for small queues the difference is minimal\nBut here’s what happens when we grow the queue\n\n
Now i’m gonna stop talking about you should avoid, and show you some good things to look at instead\n
There’s really no reason not to use a thread pool.\nNative threads have move overhead and are harder to control. Executor\n
There’s really no reason not to use a thread pool.\nNative threads have move overhead and are harder to control.\n\nYou should really go listen to Theo later, cause he will more about this.\n
If you find yourself using mutex, you’re probably doing it wrong\nSure, there are times where you REALLY need to lock, but most of the time you just need a good concurrent data structure\n\nJavas concurrency library is really good and probably has exactly what you need,\nlike atomic numerics, concurrent maps and lists\n\nAnd lastly Semaphore is a nicer and more feature complete locking mechanism if you ACTUALLY need locking. But again, think a couple of times before going for that.\n
If you find yourself using mutex, you’re probably doing it wrong\nSure, there are times where you REALLY need to lock, but most of the time you just need a good concurrent data structure\n\nJavas concurrency library is really good and probably has exactly what you need,\nlike atomic numerics, concurrent maps and lists\n\nAnd lastly Semaphore is a nicer and more feature complete locking mechanism if you ACTUALLY need locking. But again, think a couple of times before going for that.\n
If you find yourself using mutex, you’re probably doing it wrong\nSure, there are times where you REALLY need to lock, but most of the time you just need a good concurrent data structure\n\nJavas concurrency library is really good and probably has exactly what you need,\nlike atomic numerics, concurrent maps and lists\n\nAnd lastly Semaphore is a nicer and more feature complete locking mechanism if you ACTUALLY need locking. But again, think a couple of times before going for that.\n
If you find yourself using mutex, you’re probably doing it wrong\nSure, there are times where you REALLY need to lock, but most of the time you just need a good concurrent data structure\n\nJavas concurrency library is really good and probably has exactly what you need,\nlike atomic numerics, concurrent maps and lists\n\nAnd lastly Semaphore is a nicer and more feature complete locking mechanism if you ACTUALLY need locking. But again, think a couple of times before going for that.\n
If you find yourself using mutex, you’re probably doing it wrong\nSure, there are times where you REALLY need to lock, but most of the time you just need a good concurrent data structure\n\nJavas concurrency library is really good and probably has exactly what you need,\nlike atomic numerics, concurrent maps and lists\n\nAnd lastly Semaphore is a nicer and more feature complete locking mechanism if you ACTUALLY need locking. But again, think a couple of times before going for that.\n
\n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n
Shelling out is quite a lot slower in JRuby, so use in moderation. Most things don’t need shelling. For example this you can do with FileUtils.mkdir_p\nAlso look out for things like rake and capistrano shelling out\n\nPassing RubyStrings into java can be tricky, since they will be converted to java strings and you can get problems with string encoding. This is particulary important if the string isn’t really a string but a serialised object.\n\nFor example, Hot bunnies, which uses the rabbitMQ java library and wrapping it in a gem. When publishing data to rmq you need to serialize your data, send that string to java, which sends it across the network, then back into java and finally into ruby. \n