Techblog

Floorplanner's adventures in foam

Tag: floorplanner

First new Floorplanner tutorial movie: how to draw walls

In a previous post I mentioned that we bought a Yeti microphone to create tutorials with sound. However, for this first new tutorial movie we didn’t use Yeti,  because we didn’t have it yet. For the next ones we’ll use it.

Hopefully you’ll be amazed by the sound quality then!

Buy Roomstyler Credits with Bitcoin

image

At Floorplanner we like Bitcoin and that’s why you will soon be able to purchase Roomstyler Credits by paying with Bitcoin.

We are now in the final stages of testing the new system, so buying and spending Roomstyler Credits is – at this moment – limited to a small group of users. If we don’t encounter big road blocks we should be able to release it within a couple of weeks.

Roomstyler Credits, why? Well, that’s because we are going to offer premium features and those premium features won’t be free. Think of rendering photo realistic interior images at a higher resolution or requesting custom 3D furniture models or materials. And this is just the start, many more premium features will follow (for example virtual panoramas).

Most of the premium features will cost a small amount of money – only a couple of Euros – and most people don’t like going through a payment system for only a small amount of money. That’s why we are introducing credits. Buy a couple of credits once and spend them – without any friction – on all kinds of different things.

So if you are looking for a new and creative way to spend your Bitcoins, you might consider becoming an interior designer at Roomstyler.

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 – 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.

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.

© 2014 Techblog

Theme by Anders NorenUp ↑