Archive for Ruby Enterprise Edition

Hunting down obscure GC bugs

It has been approximately a year since my copy-on-write work on the Ruby garbage collector was released in a form that’s suitable for production environments, namely in the form of Ruby Enterprise Edition. In the past half year its usage has grown dramatically, being adopted by, among others, New York Times, Shopify and 37signals.

However, there are a few reports of REE freezing after a while, and it seems to occur randomly. A few people have posted backtraces of the affected Ruby processes, and the backtrace always shows the function finalize_list. Sometimes the backtrace shows that the topmost function in the call stack is st_delete, and sometimes it does not.

Is there anything more frustrating to a programmer who wants to help, than not being able to reproduce the problem? I could not reproduce it at all, not on my development machine and not on any of our servers. The problem seems to be rare because otherwise a lot more people should have reported this. There’s only one way to combat this problem: the hard way, by performing a thorough code inspection. This is not an easy task because the garbage collector source file, gc.c, consists of 2945 lines. There are about 10 lines of non-API comments, of which 7 are written by myself. There are many idoms in the Ruby code base that are hard to read.

Let’s start at st_delete, located at st.c. st.c is an implementation of a hash table, used all over the place in the Ruby codebase, for example for storing instance variables in a class. The st_delete function is for – can you guess it – deleting an entry from a hash table. Can this function loop infinitely?

C
  1. int
  2. st_delete(table, key, value)
  3.     register st_table *table;
  4.     register st_data_t *key;
  5.     st_data_t *value;
  6. {
  7.     unsigned int hash_val;
  8.     st_table_entry *tmp;
  9.     register st_table_entry *ptr;
  10.  
  11.     hash_val = do_hash_bin(*key, table);
  12.     ptr = table->bins[hash_val];
  13.  
  14.     if (ptr == 0) {
  15.         if (value != 0) *value = 0;
  16.         return 0;
  17.     }
  18.  
  19.     if (EQUAL(table, *key, ptr->key)) {
  20.         table->bins[hash_val] = ptr->next;
  21.         table->num_entries–;
  22.         if (value != 0) *value = ptr->record;
  23.         *key = ptr->key;
  24.         free(ptr);
  25.         return 1;
  26.     }
  27.  
  28.     for(; ptr->next != 0; ptr = ptr->next) {
  29.         if (EQUAL(table, ptr->next->key, *key)) {
  30.             tmp = ptr->next;
  31.             ptr->next = ptr->next->next;
  32.             table->num_entries–;
  33.             if (value != 0) *value = tmp->record;
  34.             *key = tmp->key;
  35.             free(tmp);
  36.             return 1;
  37.         }
  38.     }
  39.  
  40.     return 0;
  41. }

There is at least one infinite looping candidate in this function: the for loop in the bottom part of the function. The only way in which the ‘for’ loop can be infinite is if the linked list that the hash bucket refers to has a circular reference.

st_delete is called by run_final in gc.c. run_final runs all finalizers on a given object. Finalizers are pieces of codes that can be attached to an object. They are run some time after the garbage collector has freed an object. For example, socket objects have finalizers that close the underlying socket connection.

The run_final function looks like this:

C
  1. static void
  2. run_final(obj)
  3.     VALUE obj;
  4. {
  5.     long i;
  6.     int status, critical_save = rb_thread_critical;
  7.     VALUE args[3], table, objid;
  8.  
  9.     objid = rb_obj_id(obj);     /* make obj into id */
  10.     rb_thread_critical = Qtrue;
  11.     args[1] = 0;
  12.     args[2] = (VALUE)ruby_safe_level;
  13.     for (i=0; i<RARRAY(finalizers)->len; i++) {
  14.         args[0] = RARRAY(finalizers)->ptr[i];
  15.         if (!args[1]) args[1] = rb_ary_new3(1, objid);
  16.         rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
  17.     }
  18.     if (finalizer_table && st_delete(finalizer_table, (st_data_t*)&obj, &table)) {
  19.         for (i=0; i<RARRAY(table)->len; i++) {
  20.             VALUE final = RARRAY(table)->ptr[i];
  21.             args[0] = RARRAY(final)->ptr[1];
  22.             if (!args[1]) args[1] = rb_ary_new3(1, objid);
  23.             args[2] = FIX2INT(RARRAY(final)->ptr[0]);
  24.             rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
  25.         }
  26.     }
  27.     rb_thread_critical = critical_save;
  28. }

There are two loops in this function, and both will always terminate because RARRAY(...)->len is bounded.

run_final is called by finalize_list. This function loops through a list of objects and calls run_final on each object:

C
  1. static void
  2. finalize_list(p)
  3.     RVALUE *p;
  4. {
  5.     while (p) {
  6.         RVALUE *tmp = p->as.free.next;
  7.         run_final((VALUE)p);
  8.         /* Don’t free objects that are singletons, or objects that are already freed.
  9.          * The latter is to prevent the unnecessary marking of memory pages as dirty,
  10.          * which can destroy copy-on-write semantics.
  11.          */
  12.         if (!FL_TEST(p, FL_SINGLETON) && p->as.free.flags != 0) {
  13.             p->as.free.flags = 0;
  14.             p->as.free.next = freelist;
  15.             rb_mark_table_remove(p);
  16.             freelist = p;
  17.         }
  18.         p = tmp;
  19.     }
  20. }

This function can loop infinitely if the linked list (p) has a circular reference.

The list of possible culprits that I’ve compiled so far is not exhaustive. Although unlikely, *any* function called by one of these functions could contain an infinite loop. It’s almost impossible to review all possible code paths. Indeed, I’ve struggled with this problem for months, unable to find out the cause, or even verify that it’s indeed REE’s fault and not the fault of some native extension.

Until last week, that is. I’ve been working with Joshua Sierles, system administrator at 37signals, to solve this problem. Once in a while one of their REE processes would freeze in an infinite loop, called from finalize_list. One of the many things that we had tried is to inspect such stuck processes with gdb, but their REE binaries didn’t have debugging symbols. So after reinstalling REE with debugging symbols and after some debugging sessions, we were able to infer that the infinite loop occurs in finalize_list: the linked list contains a circular reference!

But how can such a circular reference occur? This infinite looping bug doesn’t occur in normal Ruby, so it’s probably an REE-specific bug. The fact that the infinite loop occurs in finalize_list probably means that I have a bug somewhere in the finalizers handling code, so I decided to inspect all finalizers-related code that I’ve modified. After spending a lot of time digging through the source code, I could not find any obvious places that could create circular references in the finalizers list, but I did remember an interesting change that I made in the past. Consider the following code in gc_mark_all() in the original Ruby source code:

C
  1. while (p < pend) {
  2.     if ((p->as.basic.flags & FL_MARK) &&
  3.         (p->as.basic.flags != FL_MARK)) {
  4.         gc_mark_children((VALUE)p, 0);
  5.     }
  6.     p++;
  7. }

Standard Ruby uses the FL_MARK flag in the ‘flags’ member to indicate that an object is marked by the garbage collector. This makes the above ‘if’ statement especially interesting: it checks whether FL_MARK is set, but it also checks whether FL_MARK isn’t the only flag in the ‘flags’ field. I didn’t fully understand what this means, but I did understand that because of this I can’t just get rid of the FL_MARK flag, and replace FL_MARK bitmask operations with calls to the mark table. This code here tells me that the codebase assumes that if an object is marked, then its flags is not 0 (a flag of 0 means that the object is a free slot). If I naively replace FL_MARK operations with mark table calls then this assumption will be violated. So I introduced the FL_ALLOCATED flag. Every object that is not free will have this flag set. Then I changed the ‘if’ statement to:

C
  1. if (rb_mark_table_heap_contains(heap, p) &&
  2.     (p->as.basic.flags != FL_ALLOCATED)) {
  3.     gc_mark_children((VALUE)p, 0);
  4. }

This should have the same semantics as the original, or so I thought. After all it checks whether the object is marked, but whether it’s not the case that there are no other flags set.

Consider also this code in gc_sweep(), the function that frees all non-marked objects:

C
  1. while (p < pend) {
  2.     if (!(p->as.basic.flags & FL_MARK)) {
  3.         if (p->as.basic.flags) {
  4.             obj_free((VALUE)p);
  5.         }
  6.         if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
  7.             p->as.free.flags = FL_MARK; /* remain marked */
  8.             p->as.free.next = final_list;
  9.             final_list = p;
  10.         }
  11.         else {
  12.             p->as.free.flags = 0;
  13.             p->as.free.next = freelist;
  14.             freelist = p;
  15.         }
  16.         n++;
  17.     }
  18.     else if (RBASIC(p)->flags == FL_MARK) {
  19.         /* objects to be finalized */
  20.         /* do nothing remain marked */
  21.     }
  22.     else {
  23.         RBASIC(p)->flags &= ~FL_MARK;
  24.         live++;
  25.     }
  26.     p++;
  27. }

This piece of code iterates through a Ruby heap, and frees the objects that are not marked.
if (need_call_final && FL_TEST(p, FL_FINALIZE)) { checks whether the current object has any finalizers.
The line else if (RBASIC(p)->flags == FL_MARK) { is similar to the check in gc_mark_all(). I replaced this line with else if (RBASIC(p)->flags == FL_ALLOCATED) {
I also changed the line p->as.free.flags = FL_MARK; /* remain marked */ to rb_mark_table_heap_add(heap, p); /* remain marked */

Because of the recent code inspection, I discovered that the latter change is not correct. The original code not only keeps the object marked, but it also removes all other flags. I wondered whether this may have anything to do with the infinite loop bug. So I inserted the following code before the rb_mark_table_heap_add call:

C
  1. p->as.free.flags = FL_ALLOCATED;

The semantics should now be equal to the original’s… right?

It is clear that if I do not insert this call, then the object’s existing flags will be preserved. p->as.basic.flags != FL_MARK in gc_mark_all() will then always be true and RBASIC(p)->flags == FL_MARK will always be false. This is clearly undesirable.

After some testing, it would seem that the change doesn’t break anything. The resulting Ruby interpreter was able to install the Rails gem and generate the rdoc for it, a fairly memory-intensive operation. However, I wasn’t sure whether this also tests the finalizers handling code. Then the thought that finalizers could be plain Ruby code and thus could trigger a garbage collection occurred to me. If I trigger a garbage collection inside a finalizer, will things break?

Ruby
  1. def foo
  2.         x = "hello world"
  3.         ObjectSpace.define_finalizer(x, lambda { |x| GC.start })
  4.         x = nil
  5. end
  6.  
  7. def bar
  8.         x = 1 + 1
  9.         y = x * 2
  10.         ["", "", ""]
  11.         return y * 3
  12. end
  13.  
  14. 5000.times do |i|
  15.         foo
  16. end
  17. puts "done"

Yes. Yes it will, as was clear after executing the above test script that I wrote. It would cause Ruby to abort with a message along the lines of “rb_gc_mark(): unknown data type 0×0(0xYYYY) corrupted object”. This is certainly very suspicious. Does this mean that my change w.r.t. FL_ALLOCATED is incorrect, and could be the cause of the infinite loop bug?

I spent several hours debugging this with gdb, but at the end of the day I was still as clueless as several hours ago. I knew that something went wrong, but I didn’t know why. But that was probably because I was tired. The next morning I found the solution. I spent an hour thinking about what the original code is actually trying to do, something which I didn’t fully understand before.

  1. I figured that the == FL_MARK, != FL_MARK and = FL_MARK code must have something to do with finalizers.
  2. The code around = FL_MARK is trying to keep the object alive so that it could be finalized later. This makes sense. In addition, it is apparently also a guard that prevents any garbage collection runs invoked by any of the finalizers from freeing this object. It does so by making sure that RBASIC(p)->flag == FL_MARK returns true. This prevents gc_sweep from removing the object’s marking in the ‘else’ block.
  3. In a standard mark-and-sweep implementation, all objects will be unmarked after a garbage collection run. The previous findings imply that this is not the case in Ruby’s implementation! My copy-on-write friendly mark table clears all markings after a garbage collection run, which is apparently not correct.

So setting an object’s flag to FL_MARK actually means that the object is to be finalized later, and should not be freed until its finalizer has run. == FL_MARK actually checks whether an object is marked to be finalized later, and != FL_MARK checks the negation.

With this in mind, I made the following changes: http://github.com/FooBarWidget/rubyenterpriseedition/commit/63247bf87b11c9acc2dfd1ed5f98b1ed8dd4c780
I removed the FL_ALLOCATED flag and introduced an FL_DEFER_FINALIZE flag. I replaced the FL_MARK bitmask checks with FL_DEFER_FINALIZE checks. This change has been tested in production for a while now by 37signals. Everything’s been running smoothly ever since.

I’d really like to thank Joshua for his patience. Thanks to the debugging sessions that he made possible, I was able to finally track down the cause of the infinite loop bug.

We’re going to release a new version of Ruby Enterprise Edition soon, which includes this bug fix.

It’s amazing sometimes how so much thinking work only results in 10 lines of code. If you like my work, please consider sending a donation and/or to recommend me on Working With Rails. Thank you. :)

Comments (4)

Ruby Enterprise Edition 1.8.6-20090113 released, thanks sponsors!

Ruby Enterprise Edition 1.8.6-20090113 has been released. This version is sponsored by a number of people and organizations. Please read this page for the announcement.

Comments

Progress report: Ruby Enterprise Edition documentation

One of the goals of the second Ruby Enterprise Edition sponsorship campaign is to have better documentation. I’m almost done with the documentation, and a preview is a available here.

Some REE features involve setting environment variables. Setting environment variables in the shell by calling ‘export FOO=bar’ has no permanent effect. Setting the environment variables in bashrc/profile is permanent, but doesn’t always work; for example Apache (and other processes which are typically started by the system init process) ignores bashrc/profile. There’s /etc/environment, but this seems to be Linux-specific, perhaps even Debian-specific.

Does anybody know a cross-platform method to permanently set environment variables for all processes? If no such method exists, how would one permanently set environment variables in FreeBSD, OS X and Solaris?

Comments (2)

Ruby Enterprise Edition second sponsorship campaign

Hi readers, happy 2009!

The last Ruby Enterprise Edition sponsorship campaign was a huge success, and as a result many improvements have been introduced into REE.

We’ve just launched a second sponsorship campaign with the goal of adding more improvements into REE. This time, the campaign is public so everybody can join. Please read about it here.

Click here to lend your support to: Ruby Enterprise Edition second sponsorship campaign and make a donation at www.pledgie.com !

Thank you for your support!

Comments

Ruby Enterprise Edition 1.8.6-20081205 released, thank you sponsors

Ruby Enterprise Edition (REE) is a branch of the official Ruby interpreter which is capable of reducing your Rails applications’ memory usage by 33% on average, as well as improving your applications’ performance. This is possible because REE includes copy-on-write enhancements for the garbage collector, as well as an improved memory allocator (tcmalloc). REE has been out for several months now and is already used by many high-profile websites and organizations, such as New York Times, Shopify and 37signals.

“We switched to enterprise ruby to get the full benefit of the [copy-on-write] memory characteristics and we can absolutely confirm the memory savings of 30% some others have reported. This is many thousand dollars of savings even at today’s hardware prices.” – Tobias Lütke (Shopify)

And just like Phusion Passenger, Ruby Enterprise Edition is 100% open source.

Recent developments

REE has just become better. We had been talking with DHH from 37signals about a possible sponsorship campaign for supporting REE development. The campaign has recently ended, and so we’re presenting the world with Ruby Enterprise Edition version 1.8.6-20081205.

Please read http://blog.phusion.nl/2008/12/05/ruby-enterprise-edition-186-20081205-released-thank-you-sponsors/ for the full announcement.

Comments (1)

Upcoming Ruby Enterprise Edition improvements thanks to sponsorship campaign

Wow, the community has been on fire lately. 6 months after the first introduction of Phusion Passenger (our Rails deployment utility) and Ruby Enterprise Edition (which, in combination with Phusion Passenger, allows one’s Rails applications to use 33% less memory), people are still saying good things about us. :)

Tobias Lütke from Shopify has given us a lot of praise:

“At the same time Passenger introduced some tangible improvements. We switched to enterprise ruby to get the full benefit of the [Copy-On-Write] memory characteristics and we can absolutely confirm the memory savings of 30% some others have reported. This is many thousand dollars of savings even at today’s hardware prices.”

Not only that, 37signals has recently switched Ta-da List to Phusion Passenger. According to DHH, their system administrators have been very content with Phusion Passenger.

But there’s more.

ree_fund_drive.png

We’ve been talking with DHH from 37signals about a sponsorship campaign for supporting the development of REE. We just received words that all funds have been secured. In the mean time, we had been working hard on developing REE, and so we will be releasing the improvements as well as announcing the sponsors in the very near future. The improvements are, in a nutshell:

  • Integration with the RailsBench GC patches, allowing one to tweak the garbage collector for maximum performance.
  • Better MacOS X support.
  • Better 64-bit support.
  • Better Solaris support.

Thank you, 37signals and other sponsors!

Stay tuned for more news.

Comments (8)

Who’s using Ruby Enterprise Edition in production?

A while ago someone had asked who’s using Phusion Passenger in production. The positive responses were overwhelming; thanks to all who had replied!

Ruby Enterprise Edition seems to get a bit less attention than Phusion Passenger, so we’re wondering how many people use Ruby Enterprise Edition in production. We need this information for marketing purposes, and seeing that we’re providing REE for free, we’d be really grateful if you could take some time to tell us. Please send replies to the mailing list.

And if you can, please also tell us which of your websites are powered by REE and how much traffic they get.

Thank you.

Comments (6)

Making Ruby’s garbage collector copy-on-write friendly, part 8

Hi folks, it has been a while since the last “Making Ruby’s garbage collector copy-on-write friendly” post. Many things have happened in the mean time, and my copy-on-write work is now usable (and used) in production environments, but it seems that there is still confusion. So I’ve decided to write a new post which explains the situation.

Copy-on-write updates

In March I submitted my work to the Ruby core mailing list. There has been some discussion. As a result, various people, including myself, have made a number of improvements.

The improvements are as follows:

  • The copy-on-write friendly garbage collector is now a few % faster thanks to various micro-optimizations.
  • The mark table implementation is now pluggable.

    On Windows, a copy-on-write friendly garbage collector is totally useless because fork() is not supported on Windows. Furthermore, not all Ruby applications call fork(). So I’ve made two mark table implementations: one based on the old one (which marks objects directly by setting a flag on the object) and a copy-on-write friendly one. It is now possible to change the mark table implementation during runtime by calling GC.copy_on_write_friendly = (boolean value).

    This has huge performance implications. The copy-on-write friendly mark table makes the garbage collector about 0%-20% slower, depending on the application and the workload. However, the non-copy-on-write friendly mark table is enabled by default, so by default there is only a 1% performance penalty. This performance penalty comes from the fact that marking an object now requires a function call which sets the mark flag, instead of setting the mark flag directly. But I think 1% is acceptable.

  • Various little bugs in the debugging code have been fixed.
  • Me and Ninh are working on a scientific paper regarding the copy-on-write work.

Unfortunately the discussion stranded. Matz had some concerns about performance, which is why I made the mark table implementation pluggable. I will re-submit the patch for further evaluation when the time is right.

Ruby Enterprise Edition

Many of you have probably heard of Ruby Enterprise Edition. There has been, and still is, a lot of fuss about the name. But that’s intentional and is all part of the plan — if people make a fuss about the name then it means we’re not in the Zone of Mediocrity. :)

What is Ruby Enterprise Edition? People thought it’s a closed source product, but in fact the website’s front page and download page has the following huge sticker:

(We actually added this sticker after we’ve seen that people think it’s going to be closed source.)

In one sentence:
Ruby Enterprise Edition is an easy to install Ruby interpreter that includes, among other things, my copy-on-write work.

Facts and myths:

  • It’s open source, not closed source. It’s freely available to all.
  • It’s not an entirely new Ruby implementation. It’s based on the official Ruby interpreter (MRI), version 1.8.6-p286. This means that all your existing Ruby applications are compatible with Ruby Enterprise Edition.
  • It does not only include my copy-on-write work. There’s more. Read on.
  • It is not a hostile fork, but a friendly one. The work included in Ruby Enterprise Edition is meant to be merged back to upstream at some point in the future.
  • The copy-on-write work has been submitted to the Ruby core team in the past.
  • Phusion Passenger is very well-integrated with Ruby Enterprise Edition. If you use Phusion Passenger in combination with Ruby Enterprise Edition, then your Rails applications will transparently use 33% less memory and will be faster, as if it’s magic. You don’t need to do anything special, it just works.

    The only condition is that you must not be using conservative spawning in your application. But if you don’t know what conservative spawning is then you’re not using it, and you’ll have nothing to worry about.

Why was Ruby Enterprise Edition made?

Consider the following facts:

  • My copy-on-write work can potentially save a lot of memory in Rails applications.
  • The patch has been submitted to upstream, but hasn’t been accepted yet.
  • There is a demand for lower memory usage in Rails applications, right now, not X months/years in the future.

Given the circumstances, and to satisfy the demands (including that of ourselves), we have decided that it would be best to maintain our own Ruby fork which includes these patches.

You might be wondering: Why not just release the patch? Why create a fork?

The answer is user friendliness. Telling people to download Ruby’s source code and apply a patch is not user friendly. In fact, to many people, it’s downright scary. Imagine that you want a transparent and easy way to make your Rails applications “magically” use 33% less memory. Which of the following instructions would you prefer?

Use Phusion Passenger to deploy your application. Then download the Ruby interpreter source code from www.ruby-lang.org. Download it and extract the tarball. Then, download this patch and apply it with this and that command. Then, run ‘./configure –prefix=/somewhere’. Make sure that /somewhere is not /usr in order to prevent overwriting your old Ruby installation, you don’t want that to happen. Then type ‘make’, and then ‘sudo make install’. Then download RubyGems, extract it, and type ‘sudo /somewhere/bin/ruby setup.rb’ in the RubyGems source folder. Then type ‘/somewhere/bin/gem install rails’ to install Ruby on Rails and whatever other gems you might need.

or:

Use Phusion Passenger to deploy your application. Then download Ruby Enterprise Edition. Run the installer and follow the instructions. Done.

The first one contains a lot of caveats. Many many things can go wrong. Many many people aren’t experienced in installing Ruby from source. It’s just easier if there’s a vendor that takes care of everything for you. And we are that vendor.

We want Phusion Passenger and everything surrounding it to have a “just works” experience.

So if it’s not just the copy-on-write work, then what else does Ruby Enterprise Edition include?

  • This one is huge: by using Google’s tcmalloc, an alternative memory allocator, Ruby becomes 20% faster even with the copy-on-write friendly garbage collector! Furthermore, tcmalloc seems to be more copy-on-write friendly than ptmalloc2, Linux’s default memory allocator, so by using tcmalloc we can save even more memory!
    We discovered this shortly after submitting the patch to the Ruby core mailing list. So Ruby Enterprise Edition also includes tcmalloc.
  • Ruby Enterprise Edition includes an easy-to-use installer which takes care of installing tcmalloc, Ruby, RubyGems and important/useful gems for you. It also teaches you how to tell Phusion Passenger to use Ruby Enterprise Edition instead of normal Ruby.
  • In the future we might include more patches that might be useful in production environments.

Who’s already using Ruby Enterprise Edition?

I’m not sure because we’ve never asked our users. But the Ruby on Rails Wiki is running on it, and it has been great. I’ve been monitoring the Wiki for a while now, and ever since we’ve switched it to Phusion Passenger + Ruby Enterprise Edition, it has been rock-solid (before, it used to crash often). We also observed a great reduction in memory usage.

Michael Koziarski, a Rails core developer, runs Phusion Passenger with Ruby Enterprise Edition on his blog. He said that he downgraded his server because Phusion Passenger + Ruby Enterprise Edition saved him so much memory.

Final words

I hope this post has shed some light on matters. I’m just a little surprised that there’s all this confusion going on because all of this is also documented on the Ruby Enterprise Edition website’s FAQ. eustaquiorangel.com recently interviewed me and asked similar questions. You should check it out.

I’m also a little surprised that people seem to be reluctant about installing Ruby Enterprise Edition. If I have the choice between two products A and B, and B is the same as A but is much more efficient and is easy to install, then I’d choose B.
It is that people are suspicious about our claims? We’ve published a performance and memory usage comparison. Anybody can read this comparison, perform it himself, and check whether our claims are true. Everything we claim is verifiable so I don’t understand what there is to be suspicious about.

Please feel free to post your thoughts on this, I’d really like to hear what people have to say.

Comments (11)

Ruby 1.8.6-p230/1.8.7 broke your app? Ruby Enterprise Edition to the rescue!

Ruby 1.8.6-p230/1.8.7 include fixes for the recently discovered security vulnerabilities, but they also break some apps. We’ve backported the security patches to 1.8.6-p111 and made a Ruby Enterprise Edition release based on that. See http://blog.phusion.nl/2008/06/23/ruby-186-p230187-broke-your-app-ruby-enterprise-edition-to-the-rescue/ for details.

Comments

Phusion Passenger™ 2.0 RC 1 and Ruby Enterprise Edition released

Phusion Passenger™ 2.0 RC 1 and Ruby Enterprise Edition have finally been released. See http://blog.phusion.nl/2008/06/09/phusion-passenger-20-rc-1-and-ruby-enterprise-edition-released/ for the announcement.

Comments (1)

« Previous entries Next Page » Next Page »