00:00:38.600
that I I really wanted to bring something for everybody but I was told that I was not allowed to bring durian
00:00:43.920
in here so I'm sorry
00:00:49.239
uh my name is Aaron Patterson uh and I have come from the United States to
00:00:54.760
bring you freedom
00:01:08.720
give a talk at the beginning of the conference if I can because I assume that like if I do extremely poorly at
00:01:14.759
least it happened at the beginning of the conference and everyone will have forgotten by the end of the conference
00:01:20.159
but today I'm giving the very very last talk so that kind of made me nervous and then I realized something that I I'm
00:01:26.880
giving the last talk and right now I'm the only thing that's between you and
00:01:32.920
beer so it doesn't
00:01:37.960
matter oh I'm still on mute am I on mute ah
00:01:44.600
sorry oh unmute hello is that better no
00:01:52.920
hello on mute oh it's it's
00:01:58.840
off let me try this
00:02:04.200
oh is it on now now now we're on wow
00:02:10.160
much better okay all right uh yes so I
00:02:16.120
am the only thing that is between you and beer and I also figure that once we have go to the Afterparty hopefully
00:02:21.760
after a few beers you'll have forgotten everything that I say so it'll be fine I'll be fine um at least I tell myself
00:02:29.599
that uh I work for Red Hat um I am on the manage IQ team at red hat and we manage
00:02:37.280
clouds so if you have a cloud we can manage your Cloud that is what we do
00:02:43.360
that is what our team does so if you happen to have one we will manage it for you um on the team I my job title is
00:02:50.920
hacker man uh
00:02:57.000
that's that's me uh so I'm on the Ruby core team I'm also on the rails core
00:03:03.640
team and I'm a pro Neco atsume player so I've gone
00:03:09.239
pro pro at this game this is a this is a picture of my my
00:03:15.239
setup uh I really want to say thank you to all the organizers here thank you Winston thank you all of you for coming
00:03:21.640
give yourselves a round of applause please thank you thank you for being
00:03:28.120
here uh this is an awesome conference I feel really privileged to be here for the third time it's amazing I'm so happy
00:03:35.000
to be here uh so I want to talk a little bit about my experiences here on this trip um so I I just want to you know
00:03:43.400
talk a little bit about the stuff that's happened to me while I'm in Singapore uh I had to endurian the
00:03:51.799
heat I ate a locks of food I took many selfies
00:04:00.319
so these are these are all my selfies um more selfies selfies with mats um I took
00:04:09.519
over where over 200 selfies and wees and I plan to do more of that after this
00:04:16.560
after this talk is over uh so some of the stuff that I learned I want to talk a little bit about some of the
00:04:22.440
presentations I saw here uh I learned that Ruby motion is not phone Gap
00:04:37.039
uh science is s horrific uh rails is not
00:04:44.000
Omas which uh I'm actually I'm really glad to hear about this that it's not
00:04:49.280
Omas because once I heard that it ruined sushi for me uh then I like everybody
00:04:56.080
was doing Richard Stalin stuff so I decided to Google awkward Richard Solon so that's that's what I found when I
00:05:01.960
look for that uh I learned that refinements need more
00:05:09.160
refinement so there have been there have been a lot of really really awesome talks here today so what I'm really what
00:05:15.000
I'm really trying to say here uh while you watch my presentation is that you should lower your expectations
00:05:25.360
please so yeah I'm I'm really sorry I gotta I'm GNA apologize up front really
00:05:30.919
got to apologize up front because uh all the stuff that I'm going to present to you today is like it's all very very
00:05:36.880
technical stuff like most of the stuff we're going to talk about is very Tech numbers heavy things and I'm afraid that
00:05:44.160
the content might be might be very boring it might even be super
00:05:57.919
dry so well welcome welcome welcome to my talk you may have noticed I'm trying to use so I'm trying to use in all the
00:06:05.199
slides a Baskerville font uh and the reason that I'm doing this is because I read online that uh stuff in Baskerville
00:06:14.080
if it's written in Baskerville it's more believable so I'm not making this up you can go to that URL and read about it so
00:06:21.039
I wanted I wanted people to believe the things that I have to say uh and then I just decided I should
00:06:28.280
put in a picture that I like which is this picture I I saw that on the internet I just wanted to share it
00:06:35.759
uh that's it it's just a picture I like um all right so I've got I've got a
00:06:41.960
couple cats this one this is one of my cats her name is Chuchu or uh CAC seac
00:06:48.599
uh Facebook YouTube airport is her full name uh and this is gorbach or gorby
00:06:54.840
Puff Puff Thunder horse and I actually have stickers of him so if you want a sticker of my cat come say hello to me
00:07:00.680
and I will give you a sticker of my cat um and if you want to know how I got these cats like I I I'm going to explain
00:07:20.720
uh so how if you want a c like
00:07:27.120
mine ah I I also like extremely strange keyboards too I've built like one of my
00:07:33.840
hobbies is to build keyboards I love building keyboards this is one of my keyboards I have it with me today so if
00:07:39.240
you want to check it out I'll tell you all about that and we can talk about strange keyboards um and my cats love
00:07:45.520
them too there is another one of my keyboards at home and they like to sit on it that's Choo Cho and then Gorbachev
00:07:51.560
also likes it it's really convenient while I'm programming it's right there helping me out right this is my other
00:07:59.080
one uh cho cho with my other keyboard uh recently well I guess last December my
00:08:05.440
wife and I decided to get some uh uh photos taken professional photos taken
00:08:10.680
and I wanted to share them with you these are supposed to be for holiday cards this is my wife and
00:08:15.919
I this is this is another one of me I'm not sure so I'm not sure if I actually
00:08:21.199
am supposed to own show these because I don't I don't actually own the copyright on them and I'll explain that to you
00:08:28.120
over some beers uh it's not it's an interesting story it's slightly interesting and this is
00:08:34.680
another picture of my cat just for fun um I'm really into extreme programming
00:08:39.919
so I love I love XP really love XP and when I was at rails conf I got the opportunity to meet Kent Beck which is
00:08:46.480
really awesome this is he took a photo with me this really it's really cool of him uh now
00:08:54.080
we're best friends forever uh anyway like he and and I while we are
00:08:59.920
being best friends we worked on a we collaborated together on an extreme
00:09:05.279
programming uh setup and I've been prototyping it at my apartment and this is the extreme setup so that you don't
00:09:12.880
get hurt while you are extreme programming anyway so
00:09:19.360
uh the title of my talk you might have read the in the program the title of my talk was code required but actually the
00:09:25.680
real title of my talk is uh everything you ever want to know about files but we're afraid to ask or maybe you didn't
00:09:32.519
really want to know about it anyway but you're here just because and then they told me that was too long for the
00:09:38.880
program they couldn't fit it so I had to give them a short name so this is called code required uh and I also put it in
00:09:46.680
emoji so I want to talk I want to talk a little bit about security uh this isn't
00:09:51.720
the main thing main part of the topic but we covering other stuff that you know other talks here Andre talked about
00:09:58.200
security and I want to talk talk tell a little bit a little story about uh
00:10:03.279
security stuff a little security mistake that I made uh
00:10:09.680
and this story sucks um so this is I learned this command you don't need to do this command today if you're on a
00:10:16.480
newer gate you don't need to do this one uh but this is a really really important command that you should you needed to
00:10:22.760
learn earlier uh so I'm on the security team the rail security team and I deal
00:10:29.399
with security issues that come up from time to time and basically the way that I handle those is or the way that we
00:10:34.959
handle those or my personal way that I handle it is we have a branch I'll have a branch that's like you know we got the
00:10:41.440
32 32 stable branch and that's the one that's up on GitHub and you know just for example there's all the other ones
00:10:48.040
the stable branches as well right and then I have a on my local machine I've got a 32 SE branch and I'll do that for
00:10:54.839
each of the branches that we're maintaining and I keep that branch on my machine until it's time to actually uh
00:11:02.399
do the release right when it's time to do the release I'll merge those back into the stable branch and then push
00:11:08.480
those up to GitHub anyway so I I had been working on some security stuff and
00:11:13.720
it was time you know good we're going to do the release the next day everything was going fine and I so I decided like
00:11:19.720
we had planned on doing the release the following day so I was like well I'll just do some like bug fixes now or
00:11:24.800
whatever you know just go about my work cuz we got that done so you know I'm
00:11:30.000
working against master and I decide okay I'm going to do a you know fix a bug do a commit so I do that do a commit and
00:11:36.639
then I was like okay I'm going to do get push and then as soon as I did get push
00:11:42.519
that happened and all the security patches were posted to GitHub a day
00:11:48.839
early and then I learned how to delete branches on GitHub very quickly so uh to
00:11:57.200
fix this problem you use that command that I showed earlier and I wanted to find so I was thinking I should find this I should find what happened like
00:12:04.720
the campfire conversation what happened when this you know when I did this uh so
00:12:10.880
I searched campfire for this and uh I couldn't find anything there are way too
00:12:16.320
many there are way too many uh Records in there so anyway after that I learned
00:12:21.839
how to delete branches on the remote on GitHub I did that very quickly and then I tweeted this um and everybody thought
00:12:29.800
wow that's a very handy command I should use that too and I was actually just face bombing the entire time why didn't
00:12:35.560
I do this earlier anyway so the moral of the story is that security is not fun um
00:12:43.120
and don't do open source I'm just kidding I'm just kidding
00:12:49.160
uh I'm kidding about the don't do open source part security is not fun it's it's just not fun all right so I'm on
00:12:57.120
the rails team and and I spend a lot of time thinking about
00:13:02.240
rails performance I like working on Rails performance but a lot of the time like I think a lot about boot time like
00:13:09.399
uh our application at work our application is actually open source you can go to our GitHub I don't I should
00:13:14.680
have put the URL in here but you can go to our GitHub the manage IQ GitHub and actually look at our repository so you
00:13:20.199
can see what's happening there uh and our app looks a little bit something like this we have over 500 models 83
00:13:26.959
controllers these this is just the numbers from rake stats so I don't know if it's totally accurate uh our boot
00:13:32.839
time is like 12 seconds and the way that I'm measuring boot time for this purpose is just just like this I'm running rails
00:13:39.440
Runner and just printing out the version so it takes about 12 seconds to do that and a lot of the time spent in our
00:13:45.760
application is loading files so I'm thinking about how can we load files faster so that is that's the stuff that
00:13:52.920
I've just been working on recently and that's really what this presentation is about is things I've been working on recently so before we get into how to
00:14:01.560
make this faster I want to talk about what it does today uh and then we'll talk about usage usage of loading files
00:14:09.839
how people use it uh and then we'll talk about speeding it up so first let's talk
00:14:16.040
about how files are loaded so we're going to look at three functions we're going to look at load require and
00:14:22.720
autoload and in order to talk about these functions we need to know about a few Global variables and you probably
00:14:28.320
know about these variables already so we'll look at these variables is the load path we have dollar load path
00:14:34.000
that's our first Global variable and it's just a list uh and you can modify the list you can modify it by just
00:14:40.199
saying unshift or you can use dasi and mutate it so you can actually say like
00:14:45.759
I'll run with you know I'll run- I hello and you'll see that at the top there you added that to your um added that to the
00:14:53.160
load path if you print it out now you can think of the load path is essentially our code database our code
00:14:59.639
to load database when we want to look up we want to find some code you say require Fu that's where we're going to
00:15:05.720
go look for it that database is the loaded uh the load path now the other
00:15:10.800
Global variable we need to know about is loaded features this is this is the other Global variable this is our code
00:15:17.360
that has been loaded this is just a list uh with a caveat that it's not just a
00:15:22.680
list uh it looks like a list to us Ruby programmers if you print out the global variable it's just a list but uh under
00:15:29.680
the hood inside of MRI there's actually a cache a hash of that so we can do lookups faster uh so it's not just a
00:15:37.079
list under the hood but to us it is uh this is our already loaded code database
00:15:42.839
this is the code that we've already loaded so you know when you require a file twice it's not going to be loaded twice so if we need to find if we need
00:15:50.079
to find code we look in the load path and if we need to test whether or not code has been loaded we look in loaded
00:15:55.759
features so those are our main main players when we're trying to look up files to
00:16:01.560
load all right so our first function to look at is load this is a function for
00:16:07.079
loading a file it takes a file name it also takes this wrap uh option and we'll talk about that later you can give it a
00:16:13.519
full path so you say load full path and if we run that code so we load up test
00:16:19.600
twice or test will load x. RB twice when we run that you'll see it actually loads the file twice it just takes whatever
00:16:26.720
code is in that file and just executes that thing we can also give it a VAR or a relative path so you can just say load
00:16:33.600
x. RB and in that case it will search the uh search the load path and uh load
00:16:40.880
those to find those files so if we run it you'll see uh the output is Hello World twice and you'll also notice that
00:16:46.560
I had to provide a-i the temp so that it knew where to find x. RB so that
00:16:53.000
searches the load path but it only searches the load path if you provide a file that's not an absolute path
00:17:00.360
now this this very or this option is interesting the wrap option how many of you have used
00:17:07.079
this yeah that's what I thought yes
00:17:12.160
okay so you can provide a true you can say load true now let's actually I don't
00:17:18.079
want to spoil the surprise sorry let's look at this this is our usage of it we'll say load x. RB and we can print
00:17:23.480
out X the class and on the right hand side there we'll print out the name of the class
00:17:28.919
okay and if we run this we'll see it looks exactly what you'd expect it prints out the string X because that's the name of the class and then it just
00:17:35.440
prints out X again because that's the class that was defined now if we add
00:17:40.600
true if we add true what will happen is Ruby will actually evaluate that code
00:17:46.480
inside of an anonymous module so if we execute that s this
00:17:51.799
program we'll see the output is okay module blah blah blah blah blah blah blah X and then uninitialized constant X
00:18:00.200
which is it's kind of interesting so you can actually load some code inside of an anonymous module if you want to I think
00:18:07.600
this would be interesting like let's say maybe you want to load two versions of the same library inside of an anonymous
00:18:13.200
module I think it might be interesting however you can't get access to that Anonymous module outside of the load the
00:18:19.720
caller can't get access to that the other interesting problem with this is that let's say we have uh this setup
00:18:26.120
here where test loads X and then X loads y so this evaluates that file and then
00:18:32.720
comes down and evaluates this file so we print out the names of each of those so when we execute this program we expect
00:18:38.880
the first thing to be output is uh that name and then that name so we expect y
00:18:44.960
to come out first and then X to come out second so does anyone know what the output of this will be
00:18:59.640
you might think that both of those would be wrapped in the anonymous module but it's not actually y will be Define
00:19:05.880
defined at the top level and then X will be defined in the uh inside the
00:19:11.120
anonymous module so my question is is this actually very useful and I talked to
00:19:17.520
matz about this earlier and he was like I don't know I don't know if anybody uses this so the answer
00:19:25.720
is so it seems to me like we should probably REM wrap everything wrap
00:19:31.799
everything or somehow give a module or something to that variable say wrap it in this module or remove the feature I'm
00:19:38.960
not sure what we would use that for otherwise although since nobody's using it it doesn't really matter right
00:19:45.480
right all right so load to recap load load searches the load path but it
00:19:52.559
doesn't do it doesn't have any interaction with loaded features it doesn't have any interaction with that Global variable at all only the load
00:19:58.919
path one we saw that when we gave a relative file name now if we look at
00:20:04.000
require let's have another example we can give require a full path so we'll do require twice in that second file X over
00:20:11.559
there will print out hello world and if we execute this program just as you'd expect hello world is only output once
00:20:18.240
we've all used require we all know about the semantics it'll only load that file once we can also give a relative path to
00:20:24.799
require so like this when we execute that again hello world is printed out
00:20:30.000
and you'll notice that I had to specify sltm to- so that it knew where to find
00:20:35.760
that file what else is kind of cool about require is requ require will return a Boolean to you about whether or
00:20:42.159
not that file was loaded so when we run it the first time it'll say true meaning yes I did load something I loaded some I
00:20:49.080
loaded some code the second time it'll say I didn't load any Code false is I didn't load any code so in order to do
00:20:55.960
that it has to search loaded features to figure out whether or not it already loaded that code so this is how our second Global variable is used so we can
00:21:03.240
see we can actually see that modification in action with this program right here so we dup the loaded features
00:21:08.600
before we require then print out the the difference between uh before and after
00:21:13.760
and if we look at that we'll see yes indeed it just it added that fully qualified path to the loaded features
00:21:21.159
array and you'll see like require is smart in that if we say require X and
00:21:27.520
require X . RB it'll canonicalize that file name and check the loaded features
00:21:33.559
thing so if we print that out it'll have exactly the same output as a previous slide now I keep mentioning
00:21:39.799
canonicalization and this is going to be important the load path is used for
00:21:45.080
canonicalization right so if you look at this if you look at this example on the left hand side we have
00:21:51.320
the non-canonical format and on the right hand side we have the canonical format the canonical format being the entire path of the file so Ruby knows
00:21:59.159
using using the load path how to figure out that whole file path and check the
00:22:04.799
loaded features as to whether or not it's been loaded so the the logic for this look looks a little bit something
00:22:09.840
like this is it is it canonical Ruby says is it canonical you say no it says okay we canonicalize it then we go back
00:22:17.320
to is it canonical yes we check is it loaded uh if it is isn't then we load it
00:22:23.520
add it to the loaded features and then we're done if it's already been loaded then we're done
00:22:29.039
so this canonicalization step right here is where the load path is used and this
00:22:34.720
is loaded part is where loaded features is used and then if we actually load the code loaded features is used again right
00:22:43.159
there got it it's just that simple those seven easy
00:22:50.400
steps we all got it yes okay all right so the next thing we're
00:22:57.559
going to look at at is autoload and autoload autoload usage looks like this we have we say Okay I want to autoload
00:23:03.960
some particular constant bar and when that constant is referenced for for the
00:23:09.320
first time I want you to load that constant from this particular file here so bar whenever anybody looks up bar
00:23:15.880
it'll look in file X for that thing and then when we reference the constant bar
00:23:22.240
it'll go load this program so as soon as bar is referenced it'll load x. RB that
00:23:28.080
print out high and then it'll go toine bar and we're done so when we run this
00:23:33.559
program you'll see the output is just high and then it's that constant Foo
00:23:41.279
bar so this the semantics of autoload are exactly the same as require in this
00:23:48.080
particular case so if we reference bar multiple times it's not going to load the file multiple times so if we execute
00:23:54.679
this you'll see high is printed out once and then it just prints out the con constant three times after that so the
00:23:59.919
autoload logic looks a little bit something like this did it you know when a constant is referenced it says did we
00:24:05.480
load it already uh if we didn't load it then let's go do the require logic and then we're done if we did load it then
00:24:12.840
we're done right so we do constant reference
00:24:19.400
evaluate this file and then execute the whole thing so
00:24:25.600
we know we puts out hello next now we get bar and then as soon as bar
00:24:33.039
is uh uh evalu I think I said this already ah I'm scared anyway so when bar
00:24:40.840
is referenced X isn't necessarily evaluated now the interesting thing is
00:24:47.480
we have to say like when we get autoload the autoload logic is a little bit more complicated when we say when we
00:24:53.440
reference a constant we have to say well ah let me be a little bit more clear
00:24:58.480
when we're when we're evaluating this file what actually happens is uh we're referencing that constant twice okay we
00:25:06.320
reference it once here in the Fu bar but then as soon as we're evaluating this file it comes in and it prints out high
00:25:13.200
and then it references the constant a second time here right there so what happens when
00:25:19.919
that bit of code is executed we know that we're referencing a constant a second time and we have to
00:25:25.840
say well we're currently loading this file we don't want to reload it if we reloaded it we'd be into an infinite
00:25:33.039
Loop right so bar when bar is referenced
00:25:38.559
X is not evaluated that second time so our autoload logic is a little bit more complicated we have to say okay did we
00:25:45.080
load it are we currently loading it is it in Flight if we're not currently loading it
00:25:51.799
then we do the requir logic if we are currently loading it then we have to say we're done so we need to we need some
00:25:59.559
some mechanism for making sure that we're currently loading that particular file so I'm going to get a little bit
00:26:05.120
handwavy here hand wavy but what's going on under the hood
00:26:11.080
is that we actually have a hidden Global variable that isn't exposed to Ruby there's a global variable inside of Ruby
00:26:17.399
that keeps track of the files that we're currently requiring the files that we're currently loading and that's called the
00:26:22.559
loading table and if you look inside if you look inside MRI Source you'll find this function called called get loading
00:26:28.760
table and this is the thing that keeps track of files that are currently being loaded so file load steps when we looked
00:26:36.440
at the file load steps like this right here at this load section
00:26:41.919
that's the part where we actually add to that add to that inflight list so our
00:26:47.799
load steps look a little bit something like this we take out a lock we add the file to the Loading table that is not
00:26:53.679
exposed to Ruby you don't see this normally we eval the file add that to the loaded features and then remove the
00:27:00.760
lock and remove it from unloading and we're done so I want to talk so far we've been
00:27:07.000
looking at uh functions that are just inside of Ruby we haven't talked about ruby gems at all and I want to talk a little bit about ruby gems and it's
00:27:13.760
relationship with rubies require this is important for figuring out how we're going to speed up loading
00:27:19.640
loading stuff in rails so we hit let's say we do require rack we know that we've installed the
00:27:26.480
rack Gem and when we do require rack how does it know to find rack so how do how
00:27:31.600
does it know that we can look that up or where do we look that up the way that it works is that ruby gems implements
00:27:37.360
require and if you look here you'll say like if to prove this we can get a reference to the method and say tell me
00:27:43.880
where the source location is for that method and you'll see there it's implemented somewhere inside of ruby
00:27:49.279
gems so on line 38 or whatever right there now if we run without ruby gems so
00:27:55.880
you can say disable gems and run run IRB you'll see uh the method Source location
00:28:01.000
is nil so right up there disabled gems means no ruby gems whatsoever and if we
00:28:06.399
look for the source location of require we'll see that it's nil and that means that it's implemented in C so if your
00:28:11.559
methods are implemented in Ruby you'll get a you'll get a source location for it if it's implemented in C you'll get
00:28:17.519
nil so how does ruby gems require work I'm going to boil this down very simply
00:28:23.760
it looks like this basically what it does is the alias is the alias' rubies
00:28:29.559
require off to the side then it tries to call the original require and if there is an exception then it'll go look for
00:28:36.200
any gems that have that particular file in it then it mutates the load path and
00:28:41.960
then tries the require again so I know that's a lot of code the way that it works is we say all
00:28:48.240
right try rubies require if that works great we're done we just return if an
00:28:54.000
exception happens then we say okay go find a g that contains that file then
00:29:00.399
mutate the load path to put that Gem's directory onto the load path then try
00:29:05.799
the require again and then we're done so you can see when we do require
00:29:10.960
on rack that very first that very first require is going to cause an exception
00:29:16.080
the second require will not have an exception because the gem is now put onto the load path so this very first
00:29:23.080
section here we say all right require rack lock we hit an exception and we go all all the way down through here and
00:29:28.480
we're done that second one the eag one just goes straight down to done because
00:29:34.240
we're already on the load path so there's actually a way to load
00:29:39.919
rack without causing any exceptions in your process if you do gem rack like this the gem rack actually
00:29:47.840
mutates the load path all that does is it looks up the gem mutates the load path and then both of these have no
00:29:53.559
exceptions so to tie this together we can see the exception in action if we run with- D if you run Ruby with the
00:29:59.799
debug flag on you can actually see all the exceptions that are occurring inside your app now you can see right there our
00:30:06.720
first we have one exception on rack lock and they're required a rack e tag it's fine doesn't matter anymore we can also
00:30:13.720
see this from uh inside IRB if we dup the load path and then we require rack
00:30:18.760
lock you'll see down there at the bottom that gem has been added to the load path you'll also see that the loaded features
00:30:24.960
are mutated at the same time so so far we've looked at require loading code
00:30:31.120
with require autoload and load uh We've looked at Global variables the global variables that are involved load path
00:30:37.440
loaded features and that hidden one that you don't see in Ruby land loading table and we've also looked at ruby gems as
00:30:43.039
require and how it mutates the load path so the next thing I want to look at
00:30:48.519
are Ruby Gem's usage and performance characteristics and performance improvements that we can do with ruby
00:30:54.159
gems so I wanted to know and note that I'm saying Ruby space gems usage I want
00:31:00.840
to know about people's development environments it's hard for me when I'm doing when i'm doing development against
00:31:06.440
rails and trying to improve the development environment of rails it's difficult for me because I don't have
00:31:12.639
access to all the applications that all of you are developing I have I have access to my application at work so I
00:31:19.840
use that for sure uh and then maybe some other other open- source ones but I don't know what the typical developer is
00:31:26.440
like what is the typical developer like I don't know that and that's the question that I want to answer is what
00:31:31.919
does a typical development environment look like so I created a survey thing
00:31:36.960
here this is this is the code you can go visit it you don't need to run it now because I'm going to show you the results that I have from that and
00:31:43.720
hopefully I'd like to get this thing running every year so we can see how development environments change the data
00:31:49.679
that came back looks a bit like this uh make sure to read all this I'm going to
00:32:02.559
it's kind of interesting basically all this gem does it's not even a gem it's just a script you run all it does is
00:32:08.000
collect some data about your environment and then posts it to a Google form and that goes into this this document here
00:32:15.240
uh so the data that I'm collecting are like how many gems do people use like how many gems are installed on your
00:32:21.320
system like systemwide then how many gems are in your project so I want to know like your rails project because the
00:32:28.320
gems that you use in your rails project are different than the ones that are installed globally on your system uh how many files are in each
00:32:35.519
Gem and what versions of ruby gems do you use and what versions of Ruby do you use so the reason I want to know this
00:32:42.240
data is because uh gem count impacts your performance because it modifies
00:32:47.360
that load path that load path impacts our performance and we'll see how that how that uh impacts our performance
00:32:53.360
later and the file count impacts our performance as well the way that the number of files impacts our performance
00:32:59.200
is how are we going to do caching what is our caching strategy going to be so the data specifically collected as gem
00:33:06.159
count the gem count per project and systemwide for that particular user uh your Ruby version your ruby gems version
00:33:12.799
host OS and the file counts for each of the gems that you have installed min max median
00:33:18.360
mean but nothing specific about each of the gems I also collected a unique user
00:33:23.639
ID and a unique project ID and I I think was an interesting thing that I did so I'm going to share the code with you the
00:33:30.440
user ID the unique user ID and project ID uh I put quotes around them because
00:33:35.600
they're not necessarily unique they could be duplicated basically I generated a hash for that particular
00:33:41.600
user and this is what the code looks like for generating that hash we just said okay give me your host name your IP
00:33:47.159
address your time zone and whatever your home directory is Mash that together as
00:33:52.320
a string and then shot 256 it and send it off so that's our ID so theoretically
00:33:58.320
two people who are running exactly the same setup like this could have sent duplicate results but probably that's
00:34:04.519
not true so each project I grab the per project I just grab your bundle gem file
00:34:10.280
bundler actually sets an environment variable that points at your gem file sh that and send it up so duplicates are
00:34:16.480
possible but unlikely and also this data is pretty Anonymous I don't know
00:34:22.200
anything particular about anyone who submits data so as far as responses are
00:34:27.599
concerned I got 466 unique projects 140 Unique Systems and a lot of the data I'm
00:34:33.599
going to present to you here today I used R the r programming language to do processing on it and after using R for a
00:34:47.000
uh it sucks I wasted many hours on this I would have been done way earlier if I
00:34:53.960
had just stuck with Ruby and we'll talk about that over for beerus tonight please come ask me ask me for a sticker
00:34:59.680
and tell me about and I'll tell you about how R is terrible anyway these are the versions that we looked at so this
00:35:05.720
is this is just a version breakdown we have I think I had one response using 187 so that's kind of cool uh you have
00:35:12.480
to notice that note that these uh these statistics are totally biased because the way that I advertise this is through
00:35:18.839
Twitter so they're biased towards people who follow me on Twitter uh they're also biased towards people who will actually
00:35:25.359
send me some data but I guess you know whatever those are the people who are going to get performance improvements so
00:35:30.920
good for them so our implementation systemwide we
00:35:37.079
had all I got were the only responses I got were MRI and J Ruby and that's what the breakdown looks like uh I think what
00:35:43.880
was interesting about this is that uh J Ruby users are I guess running more
00:35:48.960
projects so a person running J Ruby on their system has more projects uh than
00:35:54.760
usual um another thing I thought thought was interesting was looking at Ruby Jem's upgrades uh it turns out 44% of people
00:36:04.079
have upgraded once so 44% of the people who responded to me have upgraded their
00:36:09.440
ruby gems system and the way that I measured this is I looked at I went through every version of Ruby and looked
00:36:15.040
at the ruby gems that shipped with that version and then compared that to the ruby gems version that they told me they
00:36:20.319
were running so 44% have upgraded once and 23 23% are on the latest version so
00:36:26.440
I thought was interesting um looking at project distributions so I want to know how many
00:36:32.319
projects are on each machine this is what the project distribution look like most people are only running a couple
00:36:37.920
projects but there's a few that are running like almost 90 on one machine
00:36:43.400
right so our summary looked a little bit like this this is our Max our Max output we had 82 projects on one machine so
00:36:49.440
most people have like three at most our OS distribution looked
00:36:54.960
like this I had zero people with Windows respond to me uh everyone is using OS 10
00:37:00.520
or Linux and I'm trying to get I think that this reflects development environments we're trying to get
00:37:05.839
development environment information because that's what I care about optimizing is your development environment I don't really care about
00:37:11.440
your production environment so much I I shouldn't say don't quote me on that I do care about your prod
00:37:17.920
production environment but I'm trying to optimize your development your development environment so our project distributions
00:37:24.920
what was interesting about project distrib this is I want to look at gem distributions per project how many gems
00:37:31.040
does your project depend on and this isn't just the number of gems that are in your gem file this is the entire the
00:37:36.720
entire graph right this is what it looked like this is kind of interesting uh I'm not sure what type of
00:37:44.119
graph that is it is a graph it is a keynote
00:37:50.440
graph I was going to say oh it's linear blah blah blah I don't know it's keynote
00:37:55.480
anyway so this is this this here's a the statistics about this our Max we had somebody with 287 gems and most people
00:38:03.680
are running about 100 100 or so gems which actually works with the project we have at work our ours is a little bit on
00:38:09.839
the higher side I think we have about 200 200 Gems or so but average is about 100 gem dependencies file distribution
00:38:17.319
this is interesting this is the number of files that ruby gems thinks are
00:38:22.560
requir in a particular gem okay now look at
00:38:27.880
that that is the number of files in each gem that's not total across the project
00:38:35.599
that's each gem so you'll notice on the very right hand side there there are gems that have 14,000 files in them and
00:38:44.079
those are requir files you can actually require all of them so the average here is about 4,000 files
00:38:52.720
what I think is interesting about this is it means like there are 4,000 files that are potentially requir inside of
00:38:58.400
your project but probably not all 4,000 of them are being required system
00:39:04.440
distribution uh number of gems on each system this is interesting to me because the number of gems that are on your
00:39:10.440
system will impact uh Bin bin stubs ruby gems bin stubs so when you run bundle
00:39:17.800
exec or bundle whatever this number will impact that command so that's why I
00:39:22.960
wanted to know this so we want to optimize your projects and we also want to optimize your system summary looks
00:39:29.760
like this uh so this is the number of gems that are installed systemwide one person responded had 1200 over 12200
00:39:37.359
gems installed on their system which is crazy so a number of files number of
00:39:43.000
files per system what almost 990,000
00:39:50.400
files it's crazy there's some really interesting data out here so the average
00:39:55.440
project average project just to summarize the average project has about 100 gems about 4,000 files the average
00:40:01.400
system has about three projects on it 280 gems are typically installed on your system and maybe 13,000 files are requir
00:40:10.200
I think what this boils down to is that people typically they're just installing the gems on their system and then they
00:40:15.640
go into their projects and they're doing bundle there right so you're probably installing more gems on your system and
00:40:21.319
then using those inside of your bundles so performance characteristics
00:40:26.520
let's move on to the Future Let's talk about let's talk about the performance and how we're going to improve the performance I wanted to
00:40:33.520
know as the number of gems grows how does require change so we see we have a
00:40:40.240
range of projects here from very few gems up to many many gems uh how does
00:40:45.800
the performance of require change if we change the number of gems that are on the system so what I'm really saying here is as the load path
00:40:53.359
grows how does retire require time grow because that's what we're doing with
00:40:58.880
gems when we load the gems we put them on the load path right so what we're really talking about here is uh search
00:41:05.720
load path search time how long does it take to to search the load path and this is the test code that I use again please
00:41:12.400
read it it's you're going to be quizzed on it later I know this is a small Point font but it is Baskerville
00:41:21.359
so uh this is the test code a little zoomed in a little bit basically what I did is I said okay get uh get the clock
00:41:28.359
time the current clock time require the require the file and then get the clock
00:41:33.440
time afterwards and I if you can read this code great if not I'm going to be
00:41:38.800
posting the slides later but what I think is cool about this is we can get uh Ruby exposes a high resolution clock
00:41:44.440
to us that's also monotonic monotonic meaning uh if the system clock changes
00:41:50.880
that doesn't impact our impact our tests so what I did is I said okay we're going to incre increase the load path and
00:41:57.160
we're going to require one file and we're going to do a worst case scenario file and a best case scenario file and
00:42:03.920
we're going to graph that time and this is what the graph looks like so the red one is our our worst case scenario
00:42:09.720
that's the worst case File and the blue one is our best case the fastest one so
00:42:16.880
as you can see here uh down along the down along the x-axis there that's the number of gems we have activated so
00:42:22.960
that's roughly the size of the load path and the y axis is require time in milliseconds so how many milliseconds it
00:42:28.440
took to require that one file so you can see here we scale linearly as the size of the load path increases the amount of
00:42:34.839
time it takes to require also increases so we see like when I say worst case what does that mean well when we do like
00:42:42.000
let's say we have a load path here and we're looking for Fu the worst case means we go in here and we say oh it's
00:42:47.119
not there it's not there it's not there it's all the way at the end and we found
00:42:52.520
it now best case scenario means it's at the beginning we found it at the very
00:42:57.880
beginning it's there we're done so what I think is really interesting about this graph and it's a
00:43:04.760
question that I don't have an answer for today if it's at the beginning of that list it should be constant time it
00:43:11.040
should always be the same speed we know it's at the beginning we're done every single time yet you can see here it
00:43:17.040
actually increases linearly so I think this is a bug I don't know what the bug
00:43:22.119
is yet I don't know why it does this it's something I need to study a bit more and I can tell you when I was producing these graphs I was
00:43:29.520
crying because I was hoping I could come in here and say to you oh it's linear time everything's great but this is the
00:43:36.800
reality so at 300 gems you have it takes four to six milliseconds to require one
00:43:42.760
empty file this is an empty file and you think wow four to six milliseconds it's actually pretty fast but then when you
00:43:48.559
think oh wait my app has 3,000 files in it so 6 milliseconds times 3,000 files
00:43:55.559
adds up right so let's look at some performance improvements what I want to do is we
00:44:01.200
have this load path search that load path search is o n and I want to change it to
00:44:06.800
01 I want to do constant time constant time lookups I want to have constant time requires so searching load path is
00:44:13.520
o n and the reason searching load path is o n or we have to search load path because we keep mutating it now
00:44:20.880
searching that is you know searching that is O and I think well okay how can
00:44:26.160
we improve the performance here I think the way that we can improve performance is you know what if we just stopped
00:44:33.000
searching the load path let's just stop doing it just don't
00:44:38.200
do it anymore so how can we do that the way we do that is if you provide a full
00:44:44.839
path name to require if you provide a full path name that doesn't search the load path anymore so if you do something
00:44:52.040
like this we say Okay require Fubar baz that RB that doesn't search the load path and we can have a constant time
00:44:58.400
require in that case but the question is how can we accomplish this you don't want to write out/ fuar baz on your
00:45:04.480
system because you're shipping that code out to somebody and they might be on in a different place and also that's a pain
00:45:09.920
to write and we write Ruby for fun and writing out the entire thing like that is not fun and why are you making me
00:45:15.880
write out all this stuff this is really terrible so what we need to talk about is
00:45:21.520
canonicalization we talked about it a little bit earlier it happens in two places it happens when we search the
00:45:27.680
load path and it also happens when we search gem specs what's interesting is when you say require food like what ruby
00:45:35.040
does is it says okay I'm going to go look for food. RB I'm going to go look for f.o or food. o uh or just plain Fu
00:45:42.440
maybe there's a file named just Foo I'm going to look for that file in all those directories in the load path what's
00:45:49.079
interesting is the logic for ruby gems is required
00:45:54.359
and the logic for this is a little side thing here I found while I was researching this data is the logic for
00:46:00.359
Ruby jems and the logic for Ruby are different unfortunately so this is this
00:46:05.599
is uh using um just uh ruby gems so we say require
00:46:12.880
noir. bundle that works require n.so that works actually no I'm sorry this is
00:46:19.680
plain old Ruby now if we use that same code with ruby gems it just breaks
00:46:27.960
this is my life what I do every day for all of you so maybe I will have two beers
00:46:36.280
tonight anyway you're getting off track yes yes I am okay back to this you say require food and it calculates all these
00:46:42.359
it tries to find all these things we have a required parameter and it goes from the required parameter to the file
00:46:48.680
name right but what if we went backwards what if we went backwards from that we
00:46:54.960
know what the files are it's going to look for we can say like okay well given a particular full file path we can
00:47:02.119
predict what what the parameter to require will be we know it'll either be fu. RB or it'll be Fu or it'll be the
00:47:09.680
entire path we know this data in advance we don't need to do this at runtime so the idea is we could put together a
00:47:15.920
translation hash we could say all right let's put together a hash that says it has fu. RB and Fu in it and all those
00:47:22.920
point at lib Fu then we can change our ruby gems is requireed to instead of
00:47:28.280
looking like this where we have o n here o n here and o n here instead what we do
00:47:36.119
is we look up that parameter inside that hash we actually end up with constant
00:47:41.160
times on that two those two First Steps will be constant time all right so I put together a
00:47:48.440
little uh proof of concept for this uh ran the code exactly the same benchmarks
00:47:54.079
I showed you before except that uh this time we're looking up that file in a hash rather than scanning the
00:48:01.160
entire file system or scanning everything in the load path and this is what the graph looks like we say all
00:48:06.280
right down there along that the x-axis is the number of gems that I've activated so on the very right hand side
00:48:11.760
I've activated a th gems and on the Y AIS there is uh time in milliseconds to require one file and you can see it's a
00:48:19.000
linear time there and what I think is really really cool about this is that uh
00:48:24.119
it's less than 1 millisecond I actually had to change my test if you look at my test it says okay time this
00:48:29.960
and time it in milliseconds and it was always returning one so I actually had to measure in Nan which was fun uh but
00:48:37.559
anyway if we when can we do this when can we calculate this cash we know about
00:48:43.160
this cach on gem install so as soon as somebody installs a gem we can look at all those files and say okay here are
00:48:49.079
all the files that they installed let's calculate those short names for it put that in a hash and then when you run
00:48:55.200
your program program we can actually use that hash now astute
00:49:01.000
Watchers those of you who are still awake and not mad at me because you want
00:49:06.480
beer we'll note that there are challenges with this and I want to talk a little bit about the challenges with this that I'm trying to overcome first
00:49:12.880
off we have to deal with Dash ey people can run I showed this at the very beginning people can run with
00:49:17.960
Dashi and Dashi takes precedence over gems if you run with Dashi you want to be able to get that that file not the
00:49:25.040
one that are in gems the one that you specified on the command line we also have to deal with load path mutations
00:49:30.880
like if somebody does this I've seen this in Ruby code you might do unshift require
00:49:36.040
something now the other important thing extremely important thing is we have to have bundler support for this if we want
00:49:41.720
to have this particular strategy because the way that your applications work if you're using a bundled application like
00:49:47.280
we are at work when you say bundle exact rails whatever that rails whatever isn't
00:49:53.480
actually using ruby gems anymore Ruby jems is completely out of the picture in that case so we need to be able to
00:49:59.920
support that case too uh what happens in that case is bundler sets up the load path for you so you actually have a load
00:50:06.319
path of however big it is and then you scan against that so it would be nice as
00:50:11.760
if we could integrate this into bundler as well and then have constant time look up in that case too now I want to end
00:50:20.119
this I've been talking for a long time sorry I want to end this with a strange bug
00:50:26.559
super strange bug that I ran into while trying to figure this stuff out all right we have
00:50:32.400
two files here a. RB and b. RB and a
00:50:38.200
autoads b the same setup we had earlier but the setup before was like Temp and X
00:50:44.119
or something like that right have a here B there and if I run this program I get
00:50:51.000
an error but what's weird is it prints it out high so clear clearly I mean it's
00:50:56.240
getting into that second file but it it just gives me an error it's weird right
00:51:04.599
so I couldn't figure this out I'm looking at this what is this doing what is it doing why is this breaking does my
00:51:09.760
Cod does my code look wrong to any of you is there a bug in this code anyone see a
00:51:19.480
bug class inside class no class inside class is fine double col nope
00:51:27.599
nope is there a bug okay nobody sees a bug all right I get an
00:51:33.240
error now I get on IM with a friend of mine I'm like hey uh my code is breaking
00:51:40.799
can you help me out I'm trying to run this thing it just seems like autoload is completely broken what am I doing
00:51:47.000
wrong the conversation went went a little bit like this uh works for
00:51:53.680
me really you must have broken
00:52:00.640
something I said no no no what file names are you using what file names are you using I'm I'm shortening up this
00:52:07.359
conversation here and they they said well I'm using I'm using x. RB and y.
00:52:13.000
RV and I said try a and try a. RV and try b. RV
00:52:23.200
oh so there there was no bug in my code what what it turns out what actually breaks is if you have a file named b. RB
00:52:31.839
or rb. RB it won't
00:52:40.599
work just doesn't work so the solution is
00:52:47.240
uh don't name your fails your files like
00:52:54.240
that no no no no no no no that's that's not
00:53:00.640
that's not right that's not right you should be able to name your file b. RB or rb. RB it should work fine but what's
00:53:07.040
interesting is what I I researched this bug I dug into the internals a ruby I actually fix this bug it is a bug and
00:53:13.240
Ruby it's a bug and Ruby and I fixed it um but the problem is I
00:53:19.040
think well I was working on this bug I was looking at pairing with somebody and
00:53:24.400
pairing on fixing it and we're looking through pouring through the documentation CU I three people thought
00:53:31.880
I was doing something wrong okay I contacted three of my peers and they're like you must be doing it wrong you must
00:53:37.839
be doing it wrong autoload works for me and every one of them I had to know no
00:53:42.880
no use a. RV and b. RV so I'm pairing with somebody we're reading through reading through the documentation of
00:53:49.359
autoload and the example in the documentation of autoload uses a. RB and b.
00:54:03.119
RV anyway so I was thinking about this I was thinking about this situation for quite a while you know we fix this fix
00:54:09.760
this bug together I think as developers as developers 99% of the time we blame
00:54:15.160
ourselves like if you had run that code if you had had those two files they had. RB and b. RB and you're running that and
00:54:21.119
you're getting that error you're saying man what am I doing wrong I'm doing something wrong I'm doing something wrong and then you go along and you just
00:54:27.280
change the file name and now it works I think most of the time you just move on everyone's like well I I must have been
00:54:33.400
doing something wrong I don't know what I was doing before I don't know why I don't know what I was doing before but now it works I know you all have said
00:54:40.760
that I know every one of you have said that phrase I think we're all we're all
00:54:46.799
trained to 99% of the time blame ourselves and the thing is the reason the reason that we're trained to do that
00:54:52.920
is because 99% of the time it is our fault we did like we did we did screw something up
00:55:00.319
that's I mean that's really the truth of it I mean I know that I know that personally for myself 99% of the time I
00:55:05.920
do I do make mistakes but I think what's important is when you get errors like
00:55:11.440
that when you when you get an error like that it's really important to take the time to understand why why is it giving
00:55:17.720
me that error I I know it's a lot of work like I think it's a lot of work to
00:55:22.880
to look that up but I think if you if you do that if you're always asking why
00:55:28.240
if you're always asking why you'll become a better developer once you understand what is the source of this
00:55:34.559
because for example in this particular case it turned out to actually be a bug it was not my fault this is like the one
00:55:40.640
time the one time it was not my fault but the only way I found that out is because I persisted and looked looked
00:55:47.319
into this and kept digging and digging so what I want to encourage everybody to do is take the time to find out always
00:55:53.160
be asking why it's doing the thing that it's doing take the time to figure out why it's doing that and maybe you can
00:55:59.240
fix it and if you don't fix it if it turns out you are doing something wrong you've learned something new so with
00:56:05.280
that thank
00:56:16.200
you if we have time for questions I'll do that otherwise I'm not sure something it is
00:56:22.839
Friday right how did you fix the
00:56:30.520
oh the the answer so we have an answer over here that says regular expression that's almost that's almost true um
00:56:37.359
someone else asked me a question while I find the patch for you because it's really really it's really
00:56:44.119
amazing I have a joke about regular Expressions if you want to hear
00:56:49.559
it so a programmer had a problem and he solved it with regular expressions and now he has two problems
00:56:55.680
s that's not a joke that's
00:57:02.880
truth okay
00:57:09.760
so that that probably needs to be
00:57:17.240
bigger can everyone read that is it readable okay so here is the uh nope
00:57:24.359
that's wrong hold on let me do get
00:57:38.640
ah there we go files named
00:57:45.599
B all right there is the answer right there
00:57:52.440
that so I I'll explain I'll explain what the bug
00:57:58.680
is and it basically boils down to uh pointers in C so just as bad as regular Expressions
00:58:06.440
basically What's Happening Here is there's that uh that function there loaded feature path uh basically
00:58:11.920
iterates over everything inside of the hash so I talked earlier about the loaded features array is actually a hash
00:58:19.119
that's used as a cache this iterates through that hash calling this function looking at the file names and comparing
00:58:24.559
them to the canonicalized file name or is it no no not the canonicalized file
00:58:29.760
name the short file name now what it does is it says it's trying to take a shortcut here
00:58:35.880
everything in the loaded features path is a canonicalized file and the path that we passed in here is actually the
00:58:41.160
short name it's whatever you pass to require or whatever you pass to autoload it's that string so this particular
00:58:48.280
shortcut what it's doing is it's trying to say like okay it's trying to tell did you pass in the full path or most of the
00:58:54.720
full path and it says all right I'm going to take this key and I'm going to move the pointer all the way out to the
00:59:01.319
left hand side of the string and then I'm going to take yours move it all the way out and then I'm going to move over
00:59:06.640
one and check to see if that string is the same so since pretty much all the
00:59:11.839
files end in RB and my file name was B it would walk
00:59:18.520
back one character and compare those two and say yes I have a match
00:59:25.720
which is why rb. RB would also fail if you Tred to do autoload with RB it would
00:59:30.960
do that so basically what this did is it said okay only do this speed hack if the
00:59:36.960
file name has a dot in it so technically files named. rb. RB will now probably
00:59:49.000
that but other ones will work FS other questions you you mentioned
00:59:55.799
load and require and autoload how about require relative uh yeah so I didn't
01:00:02.359
mention I didn't mention require relative uh and that's basically because require relative all it does is figure
01:00:09.559
out the full file path and send that to require so it wasn't I mean I probably
01:00:15.440
should have put it in here for completeness but it wasn't interesting to me as far as performance improvements are
01:00:20.839
concerned also I don't particularly like require relative
01:00:27.640
should I explain why I'll ask myself questions sure all
01:00:33.359
right so I'm not I'm not a huge fan of require relative and the reason I'm not a huge fan of that is because it
01:00:38.760
calculates the full file path and basically does a require on that full file path so that is faster if you do it
01:00:44.760
if you do that that's faster because you're not doing the search of the entire load path so that's faster but
01:00:50.720
the downside of it the thing that you're giving up is that um if you change- I it
01:00:57.720
won't impact require relative so let's say for example uh I F I find this
01:01:03.400
technique to be very useful especially when dealing with Legacy code is let's say you have a file and uh you need to
01:01:10.680
test it but you don't want you you don't want to load that file you want to fake it out you have a class inside a file
01:01:16.160
that you want to fake right what you can do is you can you can provide a special path with
01:01:22.799
Dashi so let's say you have food RB in your main application but you want to replace food. RB with your
01:01:28.359
stubs you can say dasi and provide a food. RB that has your own stubs in it
01:01:34.200
and it'll load that instead of the one inside the application so it's very handy when dealing with Legacy code you can say I want to replace this one stub
01:01:41.400
out this one section of my code base and you can't do that if somebody's using require relative if they're using
01:01:46.960
require relative you'll always get that file so I'm not particularly a huge fan of
01:01:53.440
that more questions come on I'm not always in Singapore
01:01:59.119
please so the question is um what if the file system changes or uh
01:02:08.119
should we dig around in the load path if we can't look it up in the cash essentially sure uh
01:02:16.559
so I don't know uh I guess the idea the reason I
01:02:22.039
wanted to do it on install is because we know at that time we can calculate it at that particular moment uh we could do it
01:02:28.160
we could do it at runtime too I just don't know how expensive that is and probably the problem is like let's say
01:02:33.960
we have a gem that contains 4,000 files right then we'd have to calculate that cash for all 4,000 files even though
01:02:40.680
your app probably only requires a 100 of them something like that so maybe not do
01:02:46.400
it at runtime although I don't see why you couldn't I mean if we figure if we're able to calculate that cash at gem
01:02:52.680
install time we could have an option to do do it at runtime too okay if not I have a question for
01:02:58.359
you yes what day is it today Friday therefore Friday hug time
01:03:06.480
yes okay everyone are you happy that it's Friday I don't really believe that no no
01:03:13.559
come on are you happy that it's Friday are you happy that it's Friday come on we're getting beers soon
01:03:21.680
please everyone stand up I like to do it so I work at home I work remotely I
01:03:27.480
worked remotely for 5 years and I get very lonely when I work at home so what I do on Fridays I give the internet a
01:03:33.319
hug to say hello hold
01:03:48.200
on oh every okay everyone on the on the count of 3es say happy Friday one two three happy Frid
01:04:10.960
fr