Floorplanner Tech Blog
Fill shape with rotated pattern using Canvas
I was looking for a way to fill a shape with a rotated pattern using the HTML5 Canvas tag. It’s easy to fill a shape with a repeating pattern with the createPattern() method, but I had more trouble with figuring out how to get a rotated pattern.
Turns out that’s very easy too. Just rotate the canvas before calling the fill method. You can see a working sample here.
var createPattern = function(e) {
var canvas = document.getElementById(‘canvas’);
var ctx = canvas.getContext(‘2d’);
var rotation = 30;
var pattern = ctx.createPattern(img, ‘repeat’);
ctx.fillStyle = pattern;
ctx.rect(0, 0, 400, 400);
ctx.rotate(rotation * Math.PI/180);
ctx.fill();
}
var img = new Image();
img.src = ‘che.png’;
img.onload = createPattern;
Scaling our product - CDN
I’m writing a series of posts about how we scaled our product at Floorplanner.com. We started with a single server, then we created application servers in the cloud and after that we moved our databases to the cloud. This post is about our next step, about how we improved the delivery of our static files.
What files?
The floorplan drawing tool (the Editor) is a very important part of our product. It’s the place where all the floorplans are created. It comes with a 3D view, which is actually a separate application. Both apps are Flash application, so they run on the client side in the browser with the Flash Player.
Inside the 2D editor a floor plan can be decorated with furniture items and floor textures. These are all separate files in our system and we have thousands of them. We use separate files to keep the system flexible. A disadvantage of that is that it takes a lot of HTTP requests to load a floorplan with 50+ furniture items, but that’s another subject. Let’s move on.
Our file server
All these files were stored on the one machine we had. Being a file server was just another task it had to do. After we moved the website to the application nodes and the databases to the cloud, the old machine was still serving our files. The problem with this setup was that is was slow and it was a single point of failure.
The machine was located in the east of the US. For visitors from inside the US the time it took to download the 2D editor and the related files was acceptable. In Europe it was a bit slower, but also good enough. South America (especially Brazil) and Asia was a completely different story. It took 10-20 times longer to get the files to our users there. Distance matters. There was only one solution, we had to make sure that the files would be closer to our users.
CloudFront & S3
Luckily this problem was already solved by others, we had to use a content distribution network, aka CDN. Amazon had it’s own, called CloudFront. Before we could use that, we had to put our files on S3 (Simple Storage Service), the file storage solution from AWS.
“Objects are redundantly stored on multiple devices across multiple facilities in an Amazon S3 Region.” This directly solved our single point of failure issue. Although this is true in our experience, there have been some reports on S3 outages. “The number of objects you can store is unlimited.” That’s a nice plus, no need to worry about that. The number one reason for a crashing server was a full hard drive.
CDNetworks
However, we were seeing rapid growth in Brazil and Australia and CloudFront didn’t have any edge servers there (now they have one in Sao Paolo). So we still had a latency problem in those countries. Eventually that made us switch to CDNetworks which has 100 edge locations worldwide on 6 continents.
Scaling our product - Cloud database
This is the third post in a series about how we scaled our product at Floorplanner. The first one was about how it all started with one dedicated server. In the second post I talked about how we reduced some of the risk by running two application servers on separate machines in the cloud.
This post is about our database. The application servers and the load balancer were running in the AWS cloud, but the MySQL database was still on the dedicated server outside of the AWS cloud. Not really ideal. It had to be in the AWS cloud, but we wanted a bit more.
Now that our app servers were running very smooth, our new biggest risk was our database. I can’t remember if we made backups at all back in the days, but if we were, the backups were probably stored on the same disk anyway (because we only had one). We needed a proper backup system too.
Percona Server
At that time, AWS didn’t had the Relational Database Service (or it was very expensive …), so we had to do it ourselves. We got a very good tip from Valery Visnakovs from Ask.fm about using the Percona MySQL Server for our setup. Impressed by the Percona Server and their MySQL Performance Blog we choose to take it for a ride.
At the same time, we got some other good advice. Cody Faustner from Shopify told me “The very worst part of AWS, is disk I/O, so whatever you can do to minimize disk activity will go a long way.” Related, and even more important was his tip to have a server with lots of memory, enough for MySQL to load the whole working set into memory. More info about that on the MySQL Perfomance Blog.
To meet the memory demand we picked a High-Memory Extra Large Instance and installed Percona Server on it.
Backup strategy
With the new database server in place we had to think about our backup strategy. We started out with the idea to backup our database on a daily basis. But Floorplanner was being used by more and more people every day and it’d be a disaster if we’d loose a whole day of data. So we needed a better way to backup our data, a real time way. That meant replicating our data to another (mirror) server.
We created the first database server in the US east region, the same place as all the other servers. To protect our data, we didn’t want our database backup to be stored in the same region. Therefor we created a second database server in the EU west region and replicated our data there. I’ll write a short post soon about the steps we took to setup this replication.
When for some reason our main database would loose or cripple data, these changes would directly be synced to our backup database, removing important data there too. So we figured, we still needed a daily backup to reduce this risk. Amazon’s Elastic Block Store (EBS) has the perfect feature for this, snapshots. With it you can easily create a snapshot of a whole EBS block. Therefor we used and instance with EBS for the backup database server. Don’t forget to clean up your snapshots after a while, say a couple of days, otherwise your AWS bill will grow.
See below a new scheme including the database changes.
Scaling our product - Application nodes
In my previous post I talked about how we started out with only one dedicated server. This was of course a big risk. The first step we took to reduce this risk was to use multiple servers instead of only one. If there is one thing we’ve learned, it’s that a single points of failure is evil. And we had a bunch of those.
OK, but how do you split up a running system with a small team? We couldn’t do everything at once. It had to be done gradually. The idea was to tackle the biggest problems, the low hanging fruit, first. In our case it was our application server.
Application nodes
We used Mongrel to run our Ruby on Rails app and that didn’t run very smooth. It heavily leaked memory causing the system to crash every now and then. Before anything else, we needed to make this part more stable. How? By running two application servers on separate machines and by moving away from Mongrel.
We figured, if the app server itself is unreliable, then it’s better to have multiple app servers running. If one crashes, the other ones can handle the load. This gave us time to get the crashed one back up. That might keep the service online, but it didn’t fix the underlying problem, which was Mongrel. We had trouble managing its memory usage. The apps of 37signals ran on Phusion Passenger, so we gave that a try. While battling the memory load, we also switched from Apache to Nginx, because Nginx had a smaller memory foot print.
Cloud
We decided to build a very simple, and light weight application server (called an app node). One that ran Linux, Nginx, Passenger and our Rails app. We’d start with two nodes and we would run new nodes when traffic grew. The next question was, where are we going to run this setup?
We didn’t know what kind of servers we needed for our light weight app nodes. And as a startup, we didn’t want to have big upfront costs of buying actual machines. Which also had the risk of buying the wrong machines. That’s why we gave the cloud, the AWS cloud, a try.
The ‘small instance’ seemed like a good choice. But it wasn’t. We spent countless hours figuring out why everything came to a halt when the nodes had to do some actual work. After a while our conclusion was that the instance was just too small. The 1.7GB memory was just too little to run our app. The 3.75GB of the ‘medium instance’ did a much better job.
Load balancer
Unfortunately, the small instances weren’t our only problem. Now that we had two app nodes running we needed to spread the requests between them. We needed a load balancer. The with the load balancer from AWS, Elastic Load Balancing, was that it didn’t support naked domains and no HTTPS. ELB supports it both now, but not at that time. We fixed this by firing up a small instance with Nginx and its load balancing feature.
Luckily it was worth all the effort. We had a load balancer in place that spread the traffic between the two app nodes, which were connected to our database. Nginx and Passenger were running nicely together and use a lot less memory than the previous setup. However, the database was still running outside of the AWS cloud. That’s the topic for my next post.
Scaling our product - How it all started
Scaling is tough. At least, that is my conclusion after doing it for a while. According to the Startup Genome, you must be able to grow your company in five core dimensions if you want to be successful. The five core dimensions are: customers, product, team, business model and funding. I’m going to write a bit about one of those: how we scaled our product.
I assume that there are companies out there that are struggling with the scaling of their product, just like we did (and still are doing). I’m starting a series of posts about how we scaled our product. The ideas we had, the mistakes we made and things we learned. And although every company and every product is different, I hope this information will be helpful.
Floorplanner started on one machine, one dedicated server somewhere in the US. It ran Linux, Apache, Ruby on Rails and MySQL. Besides that, it also was our file server; serving the 2D and 3D apps, all the furniture items and other content. In short, it simply did everything.
We all knew that it was far from ideal, but we were in the early stage of our company. We just launched the first version of our product and we were working hard to make it better. It was a risky decision to keep everything on one server, but it was a decision we consciously made. We didn’t want to spend our time building a very scalable system, since we didn’t even know if we could make money with our product.
Yes, we wanted to build a sustainable business from the start. First a product, then finding our market fit, then scaling. It costed us some sleepless nights when the system was acting up, but I think it was the right approach for us.
In the next post I’ll talk about our first improvement. What we did to reduce the risk of having only one server.
Why Startups Fail - Infographic

(Source: amastix)
MongoDB and GridFS versioning
A few months ago we still used Amazon’s S3 service to store floor plan in a XML based file, which we call FML (Floorplanner Markup Language). There were a few big issues with using this method.
First of all, what happens when S3 goes down? We don’t have access to the data and our whole application will be useless. Another problem with S3 is that it’s slow, especially when using it inside a Rails action, because it’s using HTTP GET to retrieve the data.
For the future of Floorplanner we needed another solution to store the floor plan files. After a lot research and trying out multiple file stores, XML and Document databases, we decided to go for MongoDB and in our case GridFS.
MongoDB is a NoSQL document database and GridFS is a filesystem based on the document database principle. The best scenario would be to store our XML based files in a JSON format in MongoDB, so we can actually query all our floor plans on specific things like; which furniture items are used the most; how many rooms does each floor plan have on average.
In our situation this didn’t work well, we needed to parse the JSON code back to XML and from XML to JSON which is quiet heavy on the servers. Another problem is our own format, which relies on some hacks to make it work properly. Fortunately MongoDB provides an alternative to use, which is called GridFS (Grid File System). This way we’re able to add our FML files to Mongo and it allows us to make use of all the benefits Mongo has to offer.
Mongo helps us to make our lives easier by using versioning, replication and increased speeds over S3. Currently we are good with a Master/Slave replication, which was incredibly easy to set up (take that MySQL!). We had over 4 million files in our database and the slave was completely sync’d within a couple of hours.
Although I do not encourage to do Master/Slave replication, it’s better to use Replica Sets, but it seems fine for our situation. Versioning is build into GridFS by default. Every time you write a file with the same filename into GridFS it will be created as a new file and will also keep the old ones. If you retrieve the file it will get the lastest one. Now that’s magnificent and helps us by being able to retrieve old versions of designs if a users by accident clears her floor plan and saves it for example.
Versioning works by using the uploadDate timestamp. The lastest version is the one with the newest timestamp. There is a fantastic Rails gem that helps us to use GridFS in just a few lines of code.
There is just one little problem with that Ruby MongoDB driver gem. It doesn’t allow us to keep a limited number of versions. You can keep all the old versions or none at all, those are the two options. To solve this little issue I’ve created a monkey patch that allows you to keep a number of versions. In our system we store the last 10 designs to make sure our database size doesn’t grow out of control. But I’ve made it possible to set your own number of versions you want to keep.
# Monkey Patch for handling X number of versions
module Mongo
class GridFileSystem
def open(filename, mode, opts={})
opts = opts.dup
opts.merge!(default_grid_io_opts(filename))
if opts[:versions] && mode == 'w'
versions = opts[:versions] - 1
opts.delete(:versions)
end
file = GridIO.new(@files, @chunks, filename, mode, opts)
return file unless block_given?
result = nil
begin
result = yield file
ensure
id = file.close
unless versions.nil?
self.delete do
@files.find({'filename' => filename, '_id' => {'$ne' => id}},
:fields => ['_id'], :sort => ['uploadDate', -1], :skip => versions)
end
end
end
result
end
end
end
To use this monkey patch the syntax changes a little bit. There used to be a delete_old option, which I removed because you can also do this with the new versions option, by specifying 1 version. Here’s an example of how to use the new option:
# Writing a new version
versions = 10
@grid = Mongo::GridFileSystem.new(Mongo::Connection.new)
@grid.open("filename", "w", {:versions => versions}) do |f|
f.write "filecontent"
end
# Reading the lastest version
file = @grid.open("filename", "r") {|f| f.read }
That’s how easy it is. To put back an older version I use the Mongo console to update the timestamp of the version I want to set back. If you have any questions or remarks add a comment or send me a email at: vincent [at] floorplanner.com.
Wrong event scope in Raphael.js (and jQuery)
Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web.While playing around with this nice SVG JavaScript library Raphaël.js, I stumbled upon an issue with the event scope. Raphael doesn’t allow event handlers to run in any specific scope. This is a problem when you want to work with its event system ‘eve()’ in combination with classes. The problem is that ‘this’ inside the called method, doesn’t refer to the class instance. There is an easy workaround for it, something I remembered from the good ol’ ActionScript2 days. Just store the scope and use it when the event is triggered.
MyClass.function() {
var scope = this;
eve.on("DoSomethingEvent", function(event) {
scope.doSomething(event);
}
}
MyClass.prototype {
doSomething: function(event) {
// use original 'this'
}
}
jQuery has the same issue. You can use the same workaround in jQuery, but you can also use the jQuery.proxy() method.
$("#button").click($.proxy(function () {
// use original 'this'
},this));
Zooming in Flash Builder 4.6
In most of the apps I make there is some kind of zooming. Now that I’m playing around with the latest Flex SDK release, Flex 4.6 SDK, I need it again. So to keep myself from reinventing the wheel over and over again, I’m posting my (conceptual) zoom code here. It’s using the TransformGestureEvent which is available for Flex Mobile Projects in Flash Builder 4.6.
view.addListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
private function onZoom(e:TransformGestureEvent):void {
var bounds1:Rectangle = view.content.getBounds(view.stage);
// scale the view
view.scaleX *= e.scaleX;
view.scaleY *= e.scaleY;
var bounds2:Rectangle = view.content.getBounds(view.stage);
var dx:Number = bounds2.x - bounds1.x;
var dy:Number = bounds2.y - bounds1.y;
var dw:Number = bounds2.width - bounds1.width;
var dh:Number = bounds2.height - bounds1.height;
// move the view to keep it centered while zooming
view.x -= dx + dw/2;
view.y -= dy + dh/2;
}
Floorplanner.com is hiring!
Floorplanner was founded in 2007 with a mission to be the easiest, quickest, and best looking way to create and share interactive floor plans online. We are a small and highly innovative company based in Rotterdam, The Netherlands with a no nonsense attitude. We have over 2.5 million registered users and we are currently growing with almost 5000 new users every day.
We are building the new version of our 2D application with the latest technologies like HTML5, Flash (Molehill), WebGL etc and we are looking someone to join our efforts. If you are interested in building applications that are used by millions of people, you can contact me at gertjan [at] floorplanner.com