Tuesday, 11 February 2014

The Java/Tomcat PermGen Problem

Running the project on Tomcat, I find that it gets slower and slower, until it grinds to a halt. Java does not bother to garbage collect class definitions, as they are not expected to change much once a project is running. However, running JRuby, that is not the case, and new classes are being generated on the fly all the time. Consequently, the JVM runs out of memory (what it calls PermGen memory), and everything stops.

The solution appears to be modifying start-up options in the JVM. This can be done by running tomcat6w.exe (on Windows anyway), going to the Java tab, and adding these options:

-XX:+UseConcMarkSweepGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
Whether this is a solution remains to be seen, but I am giving it a go.
See also
http://docs.oracle.com/cd/E13209_01/wlcp/wlss30/configwlss/jvmgc.html
http://stackoverflow.com/questions/3334911/what-does-jvm-flag-cmsclassunloadingenabled-actually-do/3334954#3334954


Monday, 27 January 2014

Loading records from YAML to Rails

Occasionally you need to save data from your database, to be loaded back in at a later data, or to be loaded into another database. YAML makes saving the data trivial:

  def self.save
    File.open('data.yml', 'w') do |out|   # To file
       YAML.dump(MyRecord.all.to_a, out)
    end
  end

Loading it into Rails again is not as straighhtforward. Well, getting it into Rails is easy, getting Rails to save the data to the database is not.

There are two issues. The first is that Rails will use the SQL UPDATE method when you try to save the date. That is not going to work unless there is already a record present in the database. I found the simplest way around that s to use raw SQL to create each record with the correct ID, and then use save to update that record with the correct values (obviously you could do all that in one step, which would avoid the second issue, but require more complicated SQL).

The second issue is that ActiveRecord.save will only update attributes that are flagged as dirty, so you need to flag every column.

This is the solution I ended up with:

  def self.load
    ary = YAML.load_file "data.yml"
    ary.each do |data|
      ActiveRecord::Base.connection.execute(
            "INSERT INTO my_records (id) VALUES (#{data.id})"
      )
      MyRecord.column_names.each { |s| eval("data.#{s}_will_change!") }
      data.save
    end