Lets deploy on a Friday.

{% pullquote %}
Last Friday was meant as a wrap up day for our current project at work. {" We wanted to do quality assurance tasks, fix the last issues and go home early"}. Well, it didn’t work out like that.
{% endpullquote %}

A month ago I wrote about my debugging guidelines and best practices. This was a day were I should have sticked to the text. But it turned out different. Let me take you on a small journey through my last debugging adventure.

Everything worked in development. But what about production?

We have a Ruby on Rails application that serves a zip file. It includes several html files, zipped up together with the necessary assets. It’s consumed in a mobile application. Everything worked perfectly in development environment and I deployed the app. I was content that it should “just work”. The code was tested with an rspec test suite and the mobile app showed no problems.

{% pullquote %}
Well you probably guessed it: {"Production environment chose to behave differently"}. Once deployed, the zip file couldn’t be downloaded.
{% endpullquote %}

Check the server, correct the permissions. Not working.

I tried to download the zip file in the browser but I got an error; it wasn’t accessible to download. So I checked the server and yes, the read-permissions were wrong. I corrected them and the file could be downloaded. Only the file is generated every time the request is made (caching was to be ignored at this point). So the permissions had to be set every time the file is generated. Easy enough, just use chmod() for the file.

So I took the generated zip file and set the permissions:

f = File.open(path_to_zip, 'w+')
f.chmod(0644)

Reading this I am wondering wether I should call #close() on the file. I have to read about that. I am not sure. If you have an opinion on that, please let me know.

Now the correct permissions were set for every file generated. But: The zip file had a size of 0 bytes.

The zip file had a size of 0 bytes

So I went on an hour long debugging spree to take apart the zip-routine in my code. Somewhere in there something had to be wrong.

  • I checked wether every file I want to include was present
  • I checked wether the subfolders I wanted to create were created
  • I checked for empty files I included
  • I searched around the net if other people had these problems as well
  • I checked almost everything

It was late already. I had worked for many more hours then I had planned (though not only on this issue). I decided I couldn’t fix this issue this time and packed my things. It was Friday and I decided to take the laptop. Perhaps I could find a quiet hour on the weekend to work on this problem.

Your brain is gnawing on a problem and you can’t rest until you find a solution

I guess you know this feeling. Your brain is gnawing on a problem and you can’t rest until you find a solution. So I sat on the train and took out my laptop. I debugged in my local vagrant dev environment. I started the server in production environment and could reproduce the 0-bytes issue. I decided to try to start this debugging process with a fresh mind set: I deleted every code that wasn’t there before I encountered the problem and tried to reproduce the 0-bytes issue in dev and production environment. Aaaaaaaand: it wasn’t there.

{% pullquote %}
You, of course, already know what this means: I wrote the bug myself. {"The bug had to be in some code I wrote before."} And if you scroll up then you will find the only new code I’ve written. One of these two lines cost me over an hour.
{% endpullquote %}

Turns out in programming you should really know what you’re doing. The first line was the problem:

f = File.open(path_to_zip, 'w+')

File.open() and other methods like File.new() use IO Open Modes to determine the mode and permissions you have on your file. The choice I made was a really bad one.
I used w+ because I knew it was read-write mode. What I didn’t know was that this mode truncates the existing file to zero bytes length.

"w+" Read-write, truncates existing file to zero length
or creates a new file for reading and writing.

I should have chosen a different mode. In the end I went with a+:

"a+" Read-write, each write call appends data at end of file.
Creates a new file for reading and writing if file does
not exist.

Now it all works quite well.