00:00:00.000
ready for takeoff
00:00:17.660
all right let's get started
00:00:21.060
now
00:00:22.800
I'm sure you've all heard of
00:00:23.880
microservices the hottest new
00:00:25.859
architectural trend of the last decade
00:00:27.420
really but what is a microservice well I
00:00:30.779
think we can infer from the name that
00:00:31.980
they're like regular services but
00:00:33.180
smaller and we can infer from the hype
00:00:35.160
around them that they are like regular
00:00:36.300
services but better somehow
00:00:38.640
and as far as I'm aware this is all the
00:00:40.680
information about microservices anywhere
00:00:42.360
on the internet not a single blog post
00:00:44.100
book or think piece anywhere so we are
00:00:46.620
going to have to extrapolate ourselves
00:00:48.000
and I think we can conclude from those
00:00:49.620
two pieces of information that's smaller
00:00:51.059
is better for microservices
00:00:52.800
but what is the optimal size for a
00:00:54.780
microservice maybe each of your
00:00:56.640
microservices should encapsulate a
00:00:58.739
single concept or maybe if you're
00:01:00.600
breaking apart a monolith each of your
00:01:01.920
models should be a microservice or maybe
00:01:03.960
a single cross-cutting concern now I
00:01:06.180
would like to put this to a vote but I'm
00:01:07.380
not going to because you'd all be wrong
00:01:09.920
let's take a look at the data if regular
00:01:13.140
service oriented architecture is good
00:01:15.000
and microservices are better well any
00:01:17.460
fool can draw a line between two points
00:01:19.200
and I am such a fool so I assert that
00:01:22.259
the optimal size for a microservice in
00:01:23.880
Ruby is the single object
00:01:26.340
if you've got an object anywhere in Ruby
00:01:28.320
it should run on a remote machine but
00:01:30.540
let me be more concrete
00:01:32.280
so you've got a class and we've got an
00:01:34.740
instance of that class and I call a
00:01:36.119
method on that instance this method
00:01:37.619
should run on a remote machine this
00:01:39.119
object should be a proxy object no
00:01:40.740
matter what it is folks you may not like
00:01:42.960
it but this is what Peak architecture
00:01:44.280
looks like
00:01:45.840
I don't know how this is better than a
00:01:47.640
regular service but it must be because
00:01:49.560
microservices
00:01:51.360
and of course our custom objects should
00:01:54.360
be microservices but this is Ruby
00:01:56.040
everything's an object and therefore
00:01:57.899
everything should be a microservice this
00:01:59.579
array should be a microservice this
00:02:00.899
string should be a microservice even
00:02:02.759
these integers should run on a remote
00:02:04.619
server and to that terrible end I give
00:02:07.020
you this talk everything on microservice
00:02:09.239
the worst possible introduction to druby
00:02:14.000
thank you
00:02:16.680
now because I have the moral compass of
00:02:19.560
a Disney villain I'm going to walk you
00:02:21.120
through setting up your code to do
00:02:22.260
exactly this to make every single object
00:02:24.900
run on a remote server let's take a look
00:02:27.540
at our terrible terrible goal every
00:02:30.540
object including ones created by Ruby
00:02:33.180
automatically running on remote servers
00:02:36.120
but to get that horrible end we need to
00:02:38.160
start small we need some concept of a
00:02:40.319
remote object I want to somehow create
00:02:42.599
an object and then when I call a method
00:02:44.280
on that object have that method run
00:02:46.080
remotely
00:02:47.160
now I could do this myself maybe have
00:02:49.560
that method issue HTTP requests and then
00:02:51.720
deal with responses and have some
00:02:53.340
service set up to run that object but
00:02:54.959
that sounds like a lot of work and one
00:02:56.340
thing you should know about me is that I
00:02:57.720
am as lazy as I am irresponsible and
00:03:00.480
therefore I'm going to try to use
00:03:02.280
something built into Ruby I would like
00:03:04.500
to introduce you to druby now drewby is
00:03:08.340
an interesting little API for creating
00:03:09.959
and using remote objects it's built into
00:03:12.000
the Ruby standard Library although if
00:03:13.920
you want there is a bonus external gem
00:03:16.140
that goes with it that you can get by
00:03:17.700
installing by doing a gem install derb
00:03:20.099
and that just adds some features to the
00:03:22.560
built-in version but we're going to
00:03:23.580
stick with the built-in version now
00:03:25.319
before I go and talk about how how not
00:03:28.620
to use Ruby let's start out with the
00:03:29.940
correct way to use it so droopy has two
00:03:32.940
parts it's a sort of a client service
00:03:34.980
architecture or client server
00:03:36.599
architecture you've got some local
00:03:38.580
script let's say it's running on my
00:03:40.080
laptop and then we've got some remote
00:03:41.640
script running on a server in the cloud
00:03:43.379
somewhere well in the remote script
00:03:45.299
first require derp derb because Derby is
00:03:48.000
required by the standard Ruby standard
00:03:49.860
library but it's not required by default
00:03:51.480
so you have to require it then we set up
00:03:54.239
some class that is going to be a remote
00:03:56.519
object
00:03:57.480
next call durb.start service
00:04:00.540
and this will do what it says this will
00:04:02.159
start a droopy service on whatever
00:04:04.080
hostname import you give it
00:04:06.239
then we give it some object that's going
00:04:08.400
to handle requests for this service on
00:04:10.920
this port
00:04:11.879
and here we're just going to give an
00:04:13.980
instance of remote server next we call
00:04:17.299
durb.thread.join this will block this
00:04:19.560
script at this point so that it doesn't
00:04:21.840
immediately exit it will say stay
00:04:23.340
running and listening for incoming
00:04:24.780
connections now on the local side we
00:04:27.300
again require durb Durham then we do
00:04:29.660
dermobject.new with URI and this does
00:04:32.639
what it says on the tin this will create
00:04:34.199
a new object from whatever URI you give
00:04:37.080
it and what you want to give it is the
00:04:38.699
URI where you started your server
00:04:41.220
now this remote object this is a proxy
00:04:44.100
object
00:04:45.000
that means it's not really an instance
00:04:46.979
of remote server if I call a method.foo
00:04:49.440
that method will get proxy to the remote
00:04:51.479
server and run on the remote side and
00:04:54.180
then any any result will get returned
00:04:55.860
back to the local side let's see how
00:04:58.080
these work
00:04:59.220
I can run the remote.rb and this will
00:05:01.979
stop and wait for incoming connections
00:05:03.660
then on the local side I'd run local.rb
00:05:06.840
and this will immediately cause the foo
00:05:09.000
method to get run but it will cause it
00:05:10.680
to get run on the remote server not the
00:05:12.900
local one
00:05:14.340
this is already kind of like a little
00:05:16.380
microservice with very little code we
00:05:19.080
have a remote object
00:05:20.699
but all right that's how you're supposed
00:05:22.139
to use Ruby and it kind of sucks
00:05:25.560
I mean we have to explicitly set up the
00:05:27.660
spoon method on the remote side any
00:05:29.460
method you want to call has to be
00:05:30.960
previously set up on the remote side
00:05:32.400
before you can use it on the local side
00:05:34.620
and that's not what we want let's take a
00:05:37.080
look back at our goal we just want to
00:05:38.699
write arbitrary Ruby code and have every
00:05:40.860
object magically become a remote object
00:05:44.340
what we want is some sort of function
00:05:46.139
that will given a class like this create
00:05:48.900
a remote instance of it via magic and
00:05:51.780
then what we should get is a nice proxy
00:05:53.400
object
00:05:54.600
and we don't want to have to
00:05:56.580
pre-configure everything on the remote
00:05:57.720
side
00:05:58.680
and well if we want if we can set up any
00:06:01.560
object to be remote through Ruby why
00:06:05.220
don't we just make a remote class that
00:06:07.080
creates new objects so here I'm calling
00:06:09.060
remote I'm creating a class called
00:06:10.380
remote newer with a method make new all
00:06:12.539
it does is it takes in a class calls new
00:06:14.580
on it and then Returns the resulting
00:06:16.320
object we set that up as a ruby service
00:06:18.600
and now on my local side I can create an
00:06:21.600
instance of this remote newer using
00:06:23.160
derby object.net with URI and call make
00:06:25.560
new give it a class and I should get an
00:06:28.080
instance of this remote newer class of
00:06:29.759
this uh some class class
00:06:32.699
and this sort of works but it doesn't
00:06:34.979
quite do what we want
00:06:36.600
let's take a look at why here's what
00:06:38.639
ruby is doing if you have a remote
00:06:40.380
object and you call a method on it that
00:06:42.600
method will run on the remote side and
00:06:44.520
then anything that method returns has to
00:06:46.020
get back to the local side so droopy
00:06:48.419
will serialize whatever it's returning
00:06:50.220
in this case the string bar send it over
00:06:52.259
the wire and deserialize it on the local
00:06:54.120
side and if you do that with a string
00:06:56.160
all you get is a copy of that string a
00:06:58.440
local stray that's not what we what we
00:07:00.720
want with our make new method we want to
00:07:02.699
get another proxy object instead
00:07:05.280
well it turns out droopy is very good at
00:07:07.440
serializing simple things like strings
00:07:09.120
and integers it doesn't know how to
00:07:10.860
serialize everything though
00:07:12.600
for example if I have a remote method
00:07:14.639
that returns something weird something
00:07:16.139
that druby does not know how to serial
00:07:17.940
eyes instead of trying to return this it
00:07:20.699
will keep this instance of something
00:07:22.259
weird on the remote side and instead
00:07:24.300
return a proxy object object
00:07:28.680
and of course then if I call a method on
00:07:30.419
that it will get proxied out to the
00:07:31.919
remote side and back and that's what we
00:07:33.240
wanted with our make new function
00:07:35.220
well how does Drew me know what is too
00:07:38.099
weird to serialize well um
00:07:41.039
you can tell it you can say for a given
00:07:43.319
class include this durb Durban dumped
00:07:45.720
mix in and now Derby will never attempt
00:07:47.340
to serialize instances of this class
00:07:50.220
so all right back to our remote newer
00:07:52.680
make new nonsense now this make new
00:07:54.840
function takes a class we create a new
00:07:56.580
instance of it and we're just going to
00:07:57.720
cram in this durb Durban dumped mix in
00:07:59.759
to any object we have created
00:08:02.160
and so this works I have some class I
00:08:05.039
create a new instance in my remote newer
00:08:07.020
I use that to make a nuisance of this
00:08:08.940
class and now what I get is a derp derp
00:08:11.580
object a droopy proxy object
00:08:13.919
but this make new function it kind of
00:08:15.960
sucks it only works if the class has
00:08:18.419
takes no arguments so let's beef it up
00:08:20.639
now it can handle classes that take
00:08:22.680
multiple arguments
00:08:24.300
um keyword arguments blocks whatever and
00:08:25.919
it just passes that on to the new
00:08:27.240
function of this class
00:08:29.580
easy enough and this works I create my
00:08:32.520
remote newer and now I can call make new
00:08:34.560
and let's say we're going to make a new
00:08:35.940
string and I'll give it some initial
00:08:37.620
value of the string123 now what I get is
00:08:40.500
actually a remote string instead of a
00:08:42.180
local string and I should be able to be
00:08:44.399
able to call any method that I could
00:08:46.020
normally call on a string on this remote
00:08:47.640
string I can call 2i that 2i will get
00:08:50.760
proxied out to the remote server run and
00:08:53.220
then the result returned
00:08:54.899
but how can we prove that to ourselves
00:08:56.880
because 2i is just going to return an
00:08:58.440
integer for all we know it ran locally
00:09:00.120
well here's how uh don't worry about
00:09:02.640
this code too much all I'm doing is
00:09:04.140
monkey patching The Living Daylights out
00:09:05.580
of druby so that anytime a call it tries
00:09:08.339
to do proxy a method it will print out
00:09:09.959
what that method is
00:09:11.700
so back to our remote new nonsense we
00:09:14.640
created a remote string we called two
00:09:16.500
ions well let's see what happens on a
00:09:19.560
remote side make new is a proxied method
00:09:21.779
and so is 2i 2i did run on the remote
00:09:24.720
server this little string here this is a
00:09:27.660
tiny microservice
00:09:29.220
and so we can already make everything we
00:09:31.740
want be microservices we create a remote
00:09:34.440
newer and we can create a remote string
00:09:36.480
a remote Hash a remote instance of any
00:09:38.519
class we want
00:09:40.080
but that's already kind of lame for a
00:09:43.500
couple reasons and not just that all
00:09:45.540
we've done is add a couple hundred
00:09:46.560
millisecond delay to all of our method
00:09:48.360
calls
00:09:49.860
really notice how we have to manually
00:09:51.959
call this make new function anytime we
00:09:53.940
want to create a new instance of
00:09:55.440
anything I don't want to do that I just
00:09:57.120
want to call string.new or something
00:09:58.500
like that and get a remote object
00:10:01.800
well this is Ruby the only limits are
00:10:05.339
our imagination and our good sense
00:10:07.640
we clearly have exactly one of those
00:10:09.839
things it let's just override
00:10:12.000
string.new why not heck why stop there
00:10:14.700
let's override object.new now here I am
00:10:17.820
over we're overriding the new method on
00:10:21.000
the root object class so that instead of
00:10:23.399
returning a normal local object it will
00:10:25.380
return a remote object
00:10:28.440
and this should work string.new should
00:10:30.720
return a remote string what could go
00:10:32.880
wrong
00:10:33.899
okay well that it immediately crashes
00:10:36.420
and throws a stack Overflow error
00:10:38.519
um it turns out that druby does need to
00:10:40.620
create some objects under the hood to
00:10:42.300
function so object.new triggers droovy
00:10:45.120
triggers object on new triggers Ruby and
00:10:46.560
so on and so forth then you get an
00:10:47.579
infinite Loop
00:10:48.839
um and you know the classic way to deal
00:10:52.320
with infinite recursion is to carefully
00:10:53.940
consider your recursive cases your base
00:10:55.560
cases your edge cases and make sure that
00:10:56.940
doesn't happen but that sounds like a
00:10:58.500
lot of work so I'm just gonna inspect
00:11:00.000
the call stack and if I'm about to
00:11:01.380
recurse don't
00:11:04.040
why not
00:11:06.720
so here I am using the color method in
00:11:09.000
Ruby which is cool method it gives you
00:11:10.740
the entire stack Trace at the current
00:11:12.779
point in code as an array of strings so
00:11:15.120
we're just going to look through that
00:11:16.200
and just muck about in there and say is
00:11:18.120
anything in here drewb was I called from
00:11:20.339
druby and if so then I am at risk of
00:11:22.620
recursing and so instead of creating a
00:11:24.660
funky weird remote object we are going
00:11:26.579
to create a regular local object we do
00:11:28.920
this by before overriding object.new we
00:11:31.380
stash the original object.new method
00:11:33.240
that actually creates local objects then
00:11:35.579
if we are at risk of recursing we unbind
00:11:37.500
it and rebind it to the current objects
00:11:39.060
and then call it to CR this is all this
00:11:41.279
effort just to create a normal object
00:11:42.959
but if we are not at risk of recursing
00:11:45.540
we can do our crazy remote new object
00:11:49.200
and voila this works I can call
00:11:52.440
string.new and I get a remote proxy
00:11:55.019
object for that string same with this
00:11:56.700
array and calling 2i will run on the
00:11:59.279
remote server clung some will run on the
00:12:00.660
remote server look at this array it's a
00:12:02.760
thing of beauty I've always thought that
00:12:04.440
the only thing better than a ruby array
00:12:06.060
would be a ruby array where every method
00:12:07.920
call involves a network round trip
00:12:10.800
so all right
00:12:12.839
um I can create an array and it works
00:12:14.399
fine I can call any random method I want
00:12:16.500
not that one that one causes an error I
00:12:19.800
call the dot each and it gives me no
00:12:21.660
dump data is defined for class proc and
00:12:24.420
I swear there's a reason for this it's
00:12:25.800
not just that Drew B is getting tired of
00:12:27.240
our and let's just throwing
00:12:29.160
exceptions out of spite really it's
00:12:31.800
because each takes a block now let's
00:12:34.560
take a look at what Derby's doing under
00:12:35.940
the hood understand why that's a problem
00:12:37.980
if you have a remote object in Ruby and
00:12:40.200
you call a method on it with an argument
00:12:41.519
drew me needs to serialize this argument
00:12:43.980
send it to the remote side and have the
00:12:46.139
remote side handle it to do that it
00:12:47.940
calls marshall.dump which we'll look at
00:12:49.740
in a minute
00:12:50.820
with that argument
00:12:52.740
if you call a method on a remote object
00:12:54.779
that takes a block well a block is just
00:12:56.820
a fancy kind of argument and so droopy
00:12:59.040
will just try to dump that as a proc
00:13:02.279
now what is Marshall Marshall's Ruby's
00:13:05.339
built-in way of serializing arbitrary
00:13:07.200
objects you give it some hash like this
00:13:09.480
a colon one and call marshall.dump and
00:13:12.480
it will dump it to some data data that
00:13:14.700
you could save to a disk or send over
00:13:16.620
the wire or the network or whatever and
00:13:18.779
then later on you can reload it with
00:13:20.579
marshall.load and that should give you a
00:13:23.220
copy of the same object you put in
00:13:25.440
now here's the problem
00:13:28.100
sorry Marshall does not know how to
00:13:30.899
serialize procs
00:13:33.959
um and why would it I mean actually
00:13:37.500
sorry first martial is not unless you
00:13:39.420
realize procs the error you get is that
00:13:40.920
same error we were seeing earlier no
00:13:42.540
dump data is defined for class proc and
00:13:44.700
really why would Marshall know how to
00:13:46.200
serialize a proc procs can reference any
00:13:48.600
arbitrary Ruby code they can reference
00:13:50.779
variables outside of themselves classes
00:13:53.040
outside of themselves methods constants
00:13:54.779
globals whatever and so in order to
00:13:58.139
serialize a proc you would have to I
00:13:59.760
don't know serialize the entire program
00:14:01.200
execution context somehow so there's
00:14:03.360
just no way to coherently serialize a
00:14:05.519
plot a serialize a block so obviously we
00:14:08.579
are going to incoherently serialize one
00:14:10.260
because it turns out you can totally
00:14:12.060
tell Marshall how to serialize things it
00:14:14.040
doesn't know how to serialize you do
00:14:16.139
this by defining a couple methods on
00:14:18.060
whatever the class of whatever instance
00:14:19.860
you're trying to serialize first we have
00:14:22.380
to define a dump method and this method
00:14:24.839
should serialize whatever proc is trying
00:14:27.600
to be serialized we're going to do this
00:14:29.220
with a method called serialize block
00:14:30.540
we'll look at how that works in a second
00:14:31.980
but what it does is it's going to
00:14:33.779
serialize the current proc in into a
00:14:36.180
string representing that proc just as
00:14:38.100
raw source code basically
00:14:40.199
some people can already see the the
00:14:41.639
implications of this
00:14:44.519
and anyway if you tell Marshall how to
00:14:47.220
serialize something you have to tell it
00:14:48.480
how to deserialize something you do this
00:14:50.399
by defining a load method which takes in
00:14:52.560
the same thing your dot method put out
00:14:55.139
now given a string like this how would
00:14:57.000
you turn that into a real live in-memory
00:14:59.579
proc well you can just prepend the
00:15:01.920
string proc and evalot and you're going
00:15:04.079
to eval something like that
00:15:08.100
now
00:15:10.380
um that's how you that's teaching
00:15:11.779
Marshall how to serialize procs but you
00:15:14.820
might notice that I'm eliding a lot of
00:15:16.500
dark magic underneath this serialized
00:15:18.660
block function how does that work
00:15:21.120
poorly but this is already a talk about
00:15:24.540
things that should not be so I'll take
00:15:27.060
you through it it's a two-step process
00:15:28.980
the first is we need to find the source
00:15:30.839
of some block so we've got this proc
00:15:32.760
here we need to find the raw source code
00:15:34.680
of this and we can get part of the way
00:15:36.720
there with a method called with a some
00:15:38.760
proc dot binding.source location and
00:15:41.160
what this gives you is two pieces of
00:15:42.899
information first the file name where
00:15:44.940
this proc was defined and the line
00:15:46.500
number well if you want you can just
00:15:48.660
read that file name from disk skim down
00:15:50.760
to the appropriate line and pull out
00:15:52.079
that line why not and that's like that's
00:15:54.660
doable it gets a little messy if the
00:15:56.339
proc spans in multiple lines but it is
00:15:57.959
still possible but it's a lot of code
00:15:59.579
and I'm we've established that I'm very
00:16:01.260
lazy so instead we're going to use a gem
00:16:03.600
called method source which does exactly
00:16:05.519
what we just saw but under the hood and
00:16:07.260
for us so given some proc I can call
00:16:09.800
sunproc.source and it will give me the
00:16:11.760
source code lines for that proc as a
00:16:13.860
string
00:16:14.940
but notice how it gave us the entire
00:16:17.339
line not just the block we want just the
00:16:20.220
things from the do to the end inclusive
00:16:22.079
but it also gave us the proc method the
00:16:24.360
assignment the variable All That Jazz so
00:16:26.519
how are we going to pull out just the
00:16:27.779
block well I guess I could run this
00:16:30.300
string through an incredibly complex
00:16:31.560
regular expression but I think if I
00:16:33.420
tried to parse Ruby code with regular
00:16:34.800
Expressions I would get beaten up in the
00:16:36.540
parking lot by a gang of angry computer
00:16:38.040
science professors for my sins against
00:16:39.779
computer science so instead we're going
00:16:42.540
to run it through an actual parser there
00:16:44.699
are several nice Ruby parsers out there
00:16:46.279
the one we're going to use is syntax
00:16:48.480
tree now don't worry too much about this
00:16:50.279
code it's kind of the font is
00:16:51.720
intentionally too small to be useful
00:16:53.220
what we're doing under the hood here
00:16:54.540
with syntax tree is we start out with
00:16:56.519
some raw Ruby code
00:16:58.199
we are going to pass this to syntax tree
00:17:00.000
and say hey parse this for me and it
00:17:02.160
will do that it will turn it into a
00:17:04.140
tree-like data structure an abstract
00:17:05.760
syntax tree representing the structure
00:17:07.980
of that code
00:17:09.720
then we're going to walk that sin that
00:17:11.640
abstract syntax tree until we find
00:17:13.199
something that syntax tree the gem tells
00:17:15.600
me it tells us is a block we'll get here
00:17:18.240
in syntax which we will say yes this is
00:17:19.799
a node of type block I'll say great
00:17:21.900
let's give me everything below this
00:17:24.179
point in the tree and then format it
00:17:26.160
print it out turn it into a string and
00:17:28.980
look what we get the block itself just
00:17:31.200
what we wanted with none of the cruft
00:17:33.480
and
00:17:34.820
now uh of course if we use this to
00:17:38.400
serialize a block and this block
00:17:40.440
referenced any variables outside of it
00:17:42.240
or classes outside of it or anything
00:17:43.799
that's not in the block itself it won't
00:17:46.140
work but when have we ever let common
00:17:48.120
sense like that stop us so
00:17:50.460
let's press on remember we taught
00:17:52.320
Marshall how to serialize a block using
00:17:54.600
this serialized block function and it
00:17:57.299
works I can call marshall.dub with a
00:17:59.220
proc and it will dump something it will
00:18:00.840
give us some data well can we reload it
00:18:03.059
yes Marshall that load works we get a
00:18:05.520
reloaded proc now I can even run this
00:18:07.980
proc with three and it will print four
00:18:09.539
we did what the impossible we've
00:18:11.280
serialized the block and they said it
00:18:13.020
couldn't be done okay no one said it
00:18:15.299
couldn't be done they said it shouldn't
00:18:16.380
be done but we did it anyway and it
00:18:19.559
works we've taught Marshall how to
00:18:21.419
serialize a block and drewby just uses
00:18:23.940
Marshall so if I call radon Newt that
00:18:25.980
gets picked up and turned into a proxy
00:18:27.900
object by our previous code now I call
00:18:30.120
each and Ruby will serialize this block
00:18:32.280
and it won't throw an error this each
00:18:34.679
will get run on the remote server and
00:18:36.360
the block will get run on the remote
00:18:37.559
server and that's where one two three
00:18:38.820
will print out we are now one step
00:18:41.400
closer to the microservice architecture
00:18:43.620
that all our favorite thought leaders
00:18:45.059
tell us we need
00:18:46.620
but you might have noticed a problem and
00:18:49.679
I don't just mean the sketchy looking
00:18:51.360
guy up on stage wearing flannel
00:18:53.340
you might have noticed how we have to
00:18:54.840
explicitly call array.new I don't
00:18:57.720
usually create arrays like that I
00:18:59.520
usually would much prefer if I could use
00:19:01.380
the array literal syntax or the string
00:19:03.360
literal syntax or the hash literal
00:19:05.160
syntax
00:19:06.360
but how can we do that I mean we could
00:19:09.120
override a radon new like some kind of
00:19:10.919
maniac but we've already overrid an
00:19:12.600
object.new and this technique works if
00:19:16.380
you explicitly call a raid on you like
00:19:17.940
we've been doing but it does not work
00:19:19.799
for the array literal syntax nothing
00:19:21.720
happens when you do that and it turns
00:19:23.760
out there's just no good way to override
00:19:25.440
the literal Syntax for arrays at least
00:19:28.080
not without getting into Ruby's C code
00:19:29.640
which is a little more sketch than I'm
00:19:31.500
willing to do in this talk
00:19:32.940
so instead what if we could take the
00:19:35.340
array literal syntax and somehow
00:19:36.960
transform it into array.new syntax that
00:19:40.020
does the same thing if that more or less
00:19:42.120
well this could work but then I'd have
00:19:44.220
to run some Ruby code that reads my Ruby
00:19:45.960
code transforms it and spits out more of
00:19:47.760
Ruby code and that's I don't want to do
00:19:49.080
that I just want to run my code and have
00:19:50.580
it work
00:19:51.900
so to do that I'm going to introduce you
00:19:53.580
to the ultimate tool in every Ruby
00:19:56.280
villains toolbox for messing with syntax
00:19:59.280
data sorry um that's probably confusing
00:20:02.400
data data yes the uh the Ruby keyword
00:20:06.780
that pairs with another keyword called
00:20:08.520
underscore underscore and underscore
00:20:10.020
underscore and here's how it works
00:20:12.539
you've got some arbitrary Ruby code and
00:20:14.820
then you've got an end
00:20:16.380
and Ruby will read and execute down to
00:20:18.480
this end then ignore everything after so
00:20:20.460
you can have things that are not valid
00:20:21.840
Ruby code thereafter it's meant for data
00:20:24.179
now you're supposed to put data there
00:20:25.440
but you can also just put oh sorry I
00:20:27.660
skipped over an important bit if you
00:20:29.820
call data.read in your code you actually
00:20:32.700
get the contents that came after the end
00:20:34.919
as a string
00:20:36.299
now you're supposed to put data there
00:20:37.799
but you don't have to you can just put
00:20:39.120
more Ruby code it won't get evaluated
00:20:41.039
unless you evalot
00:20:43.020
now
00:20:44.220
at the moment this is just an incredibly
00:20:45.780
convoluted way to run a few lines of
00:20:47.280
Ruby code but the cool thing is it lets
00:20:50.160
it gives you an opportunity to run a
00:20:51.840
preprocessor on your code for example I
00:20:54.539
can take this code after the end run it
00:20:56.400
through a regular expression that finds
00:20:57.900
and replaces anything that looks like an
00:20:59.700
array and converts it to the array.new
00:21:02.400
syntax and heck I can do the same thing
00:21:04.860
for hashes and strings too why not and
00:21:07.799
this will convert each of these into
00:21:09.240
explicit dot new syntax which will get
00:21:11.280
picked up by object.new and turned into
00:21:13.440
one of those droopy proxy objects for us
00:21:15.780
now I did say that if I were to parse
00:21:19.140
Ruby code using regular Expressions as
00:21:21.480
we're doing here I would get beaten up
00:21:23.160
in the parking lot by a gang of angry
00:21:24.539
computer science professors but the talk
00:21:26.520
is almost over and I think I can make it
00:21:28.260
to the airport before they find me the
00:21:30.659
rest of you are on your own
00:21:33.120
quickly one last step
00:21:35.220
right now each of these objects will
00:21:36.900
become remote proxy objects but aren't
00:21:39.600
they really microservices
00:21:41.100
I mean they're all remote but they're
00:21:42.419
all remote to the same place our Network
00:21:44.340
diagram kind of looks like this which is
00:21:46.620
pretty lame I mean if we want our blog
00:21:48.900
post about this Cutting Edge
00:21:49.980
architecture to make it to the top of
00:21:51.720
Hacker News which is obviously the only
00:21:53.460
reason to mess with microservices it
00:21:55.320
needs to look something more like this
00:21:57.900
because as any distributed systems
00:21:59.820
engineer will tell you the more Network
00:22:01.380
hops you have in your system the faster
00:22:03.120
and more reliable it will be
00:22:05.400
so
00:22:06.480
let's rig this up so that our remote
00:22:08.159
code each remote objects runs on a
00:22:10.860
different remote server
00:22:12.720
now we're already on track to have a
00:22:14.700
really trendy hipster architecture but
00:22:16.620
going even further in that direction
00:22:17.760
let's make it serverless let's run each
00:22:20.460
of our objects in an AWS Lambda function
00:22:24.360
now
00:22:25.980
um
00:22:27.240
those of you who are familiar those of
00:22:29.159
you who are familiar with AWS Lambda
00:22:30.480
might know that lambdas are limited to
00:22:32.700
15 minutes of runtime at most which
00:22:34.620
thankfully I don't think will have
00:22:35.580
anywhere near that amount of uptime for
00:22:37.020
this architecture so we don't have to
00:22:38.760
worry about it our program will crash
00:22:40.080
well before that
00:22:41.400
no
00:22:42.900
um so let's Press On
00:22:44.220
if you're not familiar with AWS Lambda
00:22:46.380
it's a cool service by Amazon web
00:22:48.360
services you give them a function
00:22:50.159
definition and they will run it for you
00:22:51.720
whenever you ask one time a thousand
00:22:53.880
times a thousand times at once and you
00:22:56.220
don't have to worry about where it runs
00:22:57.539
you don't have to worry about it ec2
00:22:58.980
boxes or Heroku dinos or any of that
00:23:01.320
stuff and of course for the privilege
00:23:03.299
they will charge you a fraction of a
00:23:04.679
cent per second that your Lambda is
00:23:06.600
running cool anyway the basic Lambda
00:23:09.539
function definition looks like this
00:23:11.820
so let's just cram our Ruby setup into
00:23:14.520
that so I started Ruby service and then
00:23:16.620
I called derp.thread.join so there's
00:23:18.360
Lambda handle Handler which is supposed
00:23:19.799
to run for only a few seconds will block
00:23:21.840
and never complete and just listen for
00:23:23.520
incoming connections forever
00:23:25.500
um and AWS will charge me for the full
00:23:28.440
15 minutes before it times out
00:23:31.020
now a brief aside to those of you who
00:23:32.940
really know your lambdas this isn't
00:23:34.620
quite possible lambda's uh they don't
00:23:36.780
allow you to accept incoming connections
00:23:39.000
but it is possible with the magic of
00:23:41.280
tail scale unfortunately I do not have
00:23:43.500
time to go into how that works but tail
00:23:44.760
scale is really cool because really but
00:23:46.500
really that's that's devops nonsense and
00:23:48.120
we're not here for that we're here for
00:23:49.559
Ruby nonsense so back to that
00:23:52.440
so we're trying to create a remote proxy
00:23:54.960
object in a Lambda first we need to
00:23:57.179
start one of those lambdas I'm going to
00:23:59.100
do that on a a random hostname then we
00:24:02.340
need to sleep three seconds because we
00:24:04.020
need the Lambda to like have time have
00:24:06.299
time to get started and Drew me to start
00:24:09.059
listening I mean on the port then we
00:24:10.980
construct the URI that we want to
00:24:12.179
connect to to connect to that Lambda
00:24:13.740
then we can create one of our remote
00:24:16.020
newers inside of this Lambda
00:24:18.900
then we can call remote newer.make new
00:24:21.120
and create a new Pro new proxy object to
00:24:23.460
an object living in this AWS Lambda then
00:24:26.400
we have to do a little bit of hackery
00:24:27.659
because sometimes drewby gets confused
00:24:29.220
about what URI is supposed to be talking
00:24:30.480
to then we return this proxy object we
00:24:33.179
just created and now we have what we
00:24:35.280
wanted every single object remote into a
00:24:38.340
different machine for each object
00:24:39.480
running an AWS Lambda you can tell this
00:24:41.940
will be blindingly fast because I had to
00:24:43.980
add that three second sleep in there
00:24:45.179
just to make it run
00:24:47.340
so
00:24:48.659
let's see how this whole system works
00:24:50.820
this is the Unspeakable atrocity that I
00:24:53.159
promised you and this is what we've done
00:24:55.980
we pre-processed in a in what is surely
00:24:59.340
a cardinal sin of software we converted
00:25:01.320
all of our array hash and string
00:25:03.000
literals into explicit.new calls using
00:25:05.340
regular expressions
00:25:06.720
then in a further show of utter
00:25:08.280
depravity we overrode the object.new
00:25:10.559
method and then to add insult to injury
00:25:12.840
we prevented infinite recursion just by
00:25:14.580
mucking around the call stack next in
00:25:17.400
one of our only reasonably normal moves
00:25:19.740
we use druby to create remote proxy
00:25:21.900
objects then because we are incapable of
00:25:24.600
doing anything simply we've read our own
00:25:26.760
source code from disk parsed it into an
00:25:28.620
AST transformed it spit it back out into
00:25:30.659
a string and sent it over the wire all
00:25:32.460
just so that our remote objects could
00:25:33.960
handle block arguments
00:25:35.760
finally in order to be maximally hipster
00:25:39.059
we shoved the remote half of this code
00:25:40.799
into serverless AWS Lambda functions put
00:25:43.860
it all together and we've got
00:25:44.760
microservices let's see how it works and
00:25:48.600
it does
00:25:50.520
this is a remote object now and so is
00:25:53.700
this and so is this and so of this
00:25:55.380
they're all microservices ha
00:25:57.840
let's see how it works in real life I've
00:26:00.600
got some sample of arbitrary Ruby code
00:26:02.279
surely our microservice architecture
00:26:04.620
will make this blisteringly fast
00:26:07.380
okay so it takes 10 seconds to run not
00:26:09.120
so much
00:26:09.960
but surely it's a very cost effective to
00:26:12.360
run here's my Lambda bill after about a
00:26:14.700
month or so of messing around with this
00:26:17.400
um okay so it costs more than it has any
00:26:19.980
right to but surely the elegance and
00:26:22.260
simplicity of this setup would be worth
00:26:23.820
it this is all the code we've gone over
00:26:25.679
in this talk
00:26:26.940
all right so it's not so simple
00:26:29.700
relative to normal code it's slower more
00:26:32.100
expensive and hideously complex
00:26:34.559
well folks I say we've succeeded because
00:26:36.779
that is the textbook definition of a
00:26:38.460
microservice architecture
00:26:39.919
congratulations we should all feel
00:26:42.240
terrible about ourselves
00:26:47.220
how about we stick to building monoliths
00:26:49.679
and agree that the real microservices
00:26:51.120
were the friends we made along the way
00:26:54.059
now I'd like to take a few minutes for
00:26:55.980
questions and answers but because I like
00:26:57.539
the sound of my own voice entirely too
00:26:58.980
much I will be providing both
00:27:01.260
now
00:27:02.700
the first question I always get when I
00:27:04.500
present on this sort of nonsense is
00:27:06.240
should I use this in production and oh
00:27:08.820
thank you hopefully by this point the
00:27:10.559
talk the answer is clear absolutely
00:27:12.419
please do and tell me how it goes that
00:27:15.179
will be an amazing story if you have an
00:27:17.640
excess of stability performance and
00:27:19.440
maintainability this architecture will
00:27:21.299
solve that problem for you
00:27:23.520
now on a more serious note you may be
00:27:25.620
wondering what are some reasonable
00:27:26.900
non-ridiculous use cases for droopy I'd
00:27:29.400
say droopy is pretty great whenever you
00:27:31.500
for internal projects hack projects
00:27:33.900
small projects anytime you want to very
00:27:35.520
quickly get up a connection between two
00:27:37.020
chunks of Ruby code running on different
00:27:38.880
processes or even different machines
00:27:40.860
that said droopy has no built-in
00:27:42.659
security or authentication so if you
00:27:45.240
expose a derby port to the public
00:27:46.919
internet you are asking for a remote
00:27:48.840
code execution vulnerability so please
00:27:51.120
at the very least wrap it in a VPN or
00:27:53.039
something
00:27:54.179
anyway
00:27:55.559
um because some people just can't help
00:27:57.360
it but Rubberneck at a car crash I'm
00:27:59.279
sure some of you are wondering where you
00:28:00.779
can see this code in more detail and you
00:28:03.059
can find that this particular code on my
00:28:04.679
GitHub it's the same code just with more
00:28:07.200
detail with a lot more comments and
00:28:09.059
hopefully a little bit easier to read
00:28:10.500
and with a lot of the infrastructure
00:28:11.940
gaps filled in
00:28:14.220
now at this point I'm sure many of you
00:28:16.620
are wondering who in God's name are you
00:28:18.600
how did you get on stage and how can we
00:28:20.159
beef up rubyconf security so it doesn't
00:28:21.720
happen again next year
00:28:23.159
well my name is Kevin cookta you can
00:28:25.080
find me at various places online my
00:28:27.059
Twitter handle is there my personal
00:28:28.140
website is there and I've recently been
00:28:29.700
spending more time on Mastodon which you
00:28:31.140
can see in the bottom right corner
00:28:32.760
now if you're tired of hearing me talk
00:28:34.260
to myself and have a real question feel
00:28:35.820
free to hit me up after the talk I'm
00:28:37.260
always happy to chat or catch me into
00:28:39.120
town anywhere around the conference
00:28:40.799
and finally I'm sure at least a couple
00:28:42.299
of you are wondering who in God's name
00:28:43.980
would hire you and how can I avoid ever
00:28:45.900
working for them and I would like to
00:28:47.880
assure you in no uncertain terms that I
00:28:49.980
get this sort of nonsense out of my
00:28:51.240
system in my spare time so that when I
00:28:52.980
go to log on to work I write nice
00:28:54.840
reliable boring code that as far as I'm
00:28:56.640
aware none of my co-workers want to
00:28:58.380
strangle me over
00:28:59.700
that said I worked for a company called
00:29:01.200
Daybreak Health we are a fully remote
00:29:02.820
mental health company and we provide
00:29:04.620
therapy to teenagers it is easily the
00:29:07.020
most rewarding job I've ever worked at
00:29:08.520
we have literally saved people's lives
00:29:10.559
in the time that I've been there
00:29:12.600
and I don't believe we have an open
00:29:14.100
wreck at this exact moment that will
00:29:15.419
certainly change so next time you're
00:29:16.799
looking for a job feel free to reach out
00:29:18.240
I would be happy to chat about it feel
00:29:19.980
free to check us out I think we're
00:29:21.179
pretty cool
00:29:22.440
and that's the talk I hope you enjoyed
00:29:24.659
it I hope you enjoyed the rest of the
00:29:25.740
conference thank you