Making YSlow Happy With Rails, Apache, Passenger

Over the past month I’ve been obsessed with speeding up a Rails app that I perceived to be slow (http://stopfamilyviolence.org). Requests would take half a second minimum and only 50 db queries if lucky. One I saw one at 183, I went on a mission.

Most of the work was implementing fragment caching and chasing down n+1 queries. This cut the average Rails response time by 75%. It would be worth another blog post. Since it’s still not where I’d like it to be, that post will have to wait.

Meanwhile, these two things had the most impact on increasing my perception of speed (and increasing the YSlow grade from C to B). They were not in the application but in the vhosts config on Apache.

1. Set an Expires header that plays ball with Rails asset tags. I looked at several pages before I found this discussion of how to implement far future expires headers that don’t invalidate Rails asset tags.

YSlow’s Expires header component went from F to A.

Read the article, use the last code block.

2. I followed this advice and turned off ETags until I have a fine-grained reason to use them.

YSlow’s Configure ETags component went from F to A. Apparently “configure” is another word for “remove” when it comes to YSlow and ETags.

Now the site feels pretty snappy, especially if you turn off Firebug, which got really slow after I installed FireQuery.

November 15, 2009 in Ruby on Rails

TimeCard App For BaseCamp

This is an Adobe AIR app for keeping your weekly timesheet in Basecamp. Like other Basecamp time apps, it has a timer and allows you to add time without having to jump from project to project, todo to todo, to add your time. Unlike others, it gives you the goodness of the standard basecamp time report.

Download it here:
Timecard.air

Please let me know what you think, bugs you encounter, and ideas you have.

Updates:

1.1 – major new update! The application now has an icon and checks for updates when you ask it to. You can edit time entries and move them from todo item to todo item also.
1.054 – added company name to project dropdown, fixed bug whereby new time entries had gray background
1.053 – bugfix re: todos not being shown when creating new time entries, removed tabs and moved into application menu, since AIR lets you do that kind of thing
1.05 – a global search went too far and caused authentication to stop working…
1.04 – primarily bugfixes and refactoring – removed edit button from time entry until I figure out why it doesn’t work
1.03 – added ability to create new todo items when adding a time entry – only works for existing todo lists – IF YOU WERE USING 1.02 YOU MAY HAVE TO Reload->Everything TO GET IT TO WORK
1.02 – added menu with ability to reload data from basecamp and page from week to week
1.01 – removed the AIR Introspector and fixed some bugs

Hints:

1. If, after you enter your basecamp info, it never loads, please let me know.

2. If you use OpenID, you can go to your basecamp “My Info” page and view your secret username and password, which allows API access.

3. You can reload projects and time entries by using the Reload menu.

4. You can jump from week to week using the View menu.

5. Some users have an issue whereby after you enter your basecamp info, it just sits there. Please check what it says in the upper right status bar and fill out the contact form. I developed it using Mac OSX 10.5.8, and tested it in Windows XP.

September 22, 2009 in Uncategorized

Thinking Sphinx with Excerpt Highlighting

So you want to highlight search terms and excerpt relevant phrases in your search results, and you want it done using the same stemming rules that your search engine uses to gather results?

Update 1/18/10: The newer version of Thinking Sphinx include support for excerpting and don’t work with the patch I present below. The patch worked in 1.13.5, but doesn’t work in 1.13.14. If you want the functionality presented here but have a modern version, try installing the plugin:

script/plugin install git@github.com:dfurber/thinking_sphinx_excerpts.git

Thinking Sphinx has the advantage of delta indexing. Ultrasphinx has lots of cool features such as excerpt highlighting. Wouldn’t it be nice if you could have TS’s delta indexing and search highlighting?

As it turns out, both Thinking Sphinx and Ultrasphinx are not only wrappers around Sphinx, but around another plugin that does the actual interfacing with Sphinx, called Riddle.

Riddle has an excerpt method (along with many other cool toys that I haven’t played with yet) that Ultrasphinx exposes but Thinking Sphinx does not.

It turned out to be straightforward to take the excerpts method out of US and stick it into TS. Using the patched TS, the way to have your search terms excerpted and highlighted is simply as follows.

For a single model:

User.search "david", :excerpts => true

For multiple models:

ThinkingSphinx.Search.search "david", :excerpts => true, :classes => [User, Post, Photo, Event]

The patch for Paginating Find:

Thinking Sphinx patch for paginating_find

I used to be a fan of paginating_find over will_paginate. This is the version I use in production. It performs the search, runs the results through Sphinx’s excerpt highlighter, and returns the results wrapped in a Paging Enumerator.

class Object
  def _metaclass 
    class << self
      self
    end
  end
end

module ThinkingSphinx
  class Search
    class << self
      # Overwrite the configured content attributes with excerpted and highlighted versions of themselves.
      # Runs run if it hasn't already been done.
      def excerpts(results, client, parsed_query)
        return if results.empty? or client.nil?
        options = {
            :before_match => '',
            :after_match => '',
            :chunk_separator => "…",
            :limit => 256,
            :around => 5
        }
        content_methods = %w{title name description} # the attributes of any model you would like to have excerpted
        # See what fields in each result might respond to our excerptable methods
        results_with_content_methods = results.map do |result|
          [result, 
          content_methods.map do |methods|
            methods.detect do |this| 
              result.respond_to? this
            end
          end
          ]
        end

        # Fetch the actual field contents
        docs = results_with_content_methods.map do |result, methods|
          methods.map do |method| 
            method and strip_bogus_characters(result.send(method)) or ""
          end
        end.flatten

        excerpting_options = {
          :docs => docs,         
          :index => "user_core", #MAIN_INDEX, # http://www.sphinxsearch.com/forum/view.html?id=100
          :words => strip_query_commands(parsed_query.to_s)
        }.merge(options)

        responses = client.excerpts(excerpting_options)

        responses = responses.in_groups_of(content_methods.size)

        results_with_content_methods.each_with_index do |result_and_methods, i|
          # Override the individual model accessors with the excerpted data
          result, methods = result_and_methods
          methods.each_with_index do |method, j|
            data = responses[i][j]
            if method
              result._metaclass.send('define_method', method) { data }
              attributes = result.instance_variable_get('@attributes')
              attributes[method] = data if attributes[method]
            end
          end
        end

        results = results_with_content_methods.map do |result_and_content_method| 
          result_and_content_method.first.freeze
        end

        results
      end  


      def search_with_excerpts_and_pagination(*args)
        query = args.clone  # an array
        options = query.extract_options!

        retry_search_on_stale_index(query, options) do
          results, client = search_results(*(query + [options]))

          ::ActiveRecord::Base.logger.error(
            "Sphinx Error: #{results[:error]}"
          ) if results[:error]

          klass   = options[:class]
          page    = options[:page] ? options[:page].to_i : 1
          total = results[:total]

          results = ThinkingSphinx::Collection.create_from_results(results, page, client.limit, options)

          if options[:excerpts] and !results.empty?
            results = excerpts(results, client, query)
          end

          if options[:page]
            PagingEnumerator.new(client.limit, total, false, page, 1) do |pg|
              results
            end
          else
            results
          end
        end
      end
      alias_method_chain :search, :excerpts_and_pagination

      def strip_bogus_characters(s)
        # Used to remove some garbage before highlighting
        s.gsub(/<.*?>|\.\.\.|\342\200\246|\n|\r/, " ").gsub(/http.*?( |$)/, ' ') if s
      end

      def strip_query_commands(s)
        # XXX Hack for query commands, since Sphinx doesn't intelligently parse the query in excerpt mode
        # Also removes apostrophes in the middle of words so that they don't get split in two.
        s.gsub(/(^|\s)(AND|OR|NOT|\@\w+)(\s|$)/i, "").gsub(/(\w)\'(\w)/, '\1\2')
      end 


    end
  end
end

The Will Paginate Version

Please asked about a will_paginate version, so I put this together and tested it. It performs the query on Sphinx, then does the highlighting, and passes the results array through Thinking Sphinx’s own will_paginate collection class.

Thinking Sphinx patch for will_paginate

class Object
  def _metaclass 
    class << self
      self
    end
  end
  
end

module ThinkingSphinx
  class Search
    class << self
      # Overwrite the configured content attributes with excerpted and highlighted versions of themselves.
      # Runs run if it hasn't already been done.
      def excerpts(results, client, parsed_query)
        return if results.empty? or client.nil?
        options = {
            :before_match => '',
            :after_match => '',
            :chunk_separator => "…",
            :limit => 256,
            :around => 5
        }
        content_methods = %w{title name description} # the attributes of any model you would like to have excerpted
        # See what fields in each result might respond to our excerptable methods
        results_with_content_methods = results.map do |result|
          [result, 
          content_methods.map do |methods|
            methods.detect do |this| 
              result.respond_to? this
            end
          end
          ]
        end

        # Fetch the actual field contents
        docs = results_with_content_methods.map do |result, methods|
          methods.map do |method| 
            method and strip_bogus_characters(result.send(method)) or ""
          end
        end.flatten

        excerpting_options = {
          :docs => docs,         
          :index => "user_core", #MAIN_INDEX, # http://www.sphinxsearch.com/forum/view.html?id=100
          :words => strip_query_commands(parsed_query.to_s)
        }.merge(options)

        responses = client.excerpts(excerpting_options)

        responses = responses.in_groups_of(content_methods.size)

        results_with_content_methods.each_with_index do |result_and_methods, i|
          # Override the individual model accessors with the excerpted data
          result, methods = result_and_methods
          methods.each_with_index do |method, j|
            data = responses[i][j]
            if method
              result._metaclass.send('define_method', method) { data }
              attributes = result.instance_variable_get('@attributes')
              attributes[method] = data if attributes[method]
            end
          end
        end
        results.results = results_with_content_methods.map do |result_and_content_method| 
          result_and_content_method.first.freeze
        end

        results
      end  


      def search_with_excerpts(*args)
        query = args.clone  # an array
        options = query.extract_options!

        retry_search_on_stale_index(query, options) do
          results, client = search_results(*(query + [options]))

          ::ActiveRecord::Base.logger.error(
            "Sphinx Error: #{results[:error]}"
          ) if results[:error]

          klass   = options[:class]
          page    = options[:page] ? options[:page].to_i : 1
          total = results[:total]

          results = ThinkingSphinx::Collection.create_from_results(results, page, client.limit, options)

          if options[:excerpts] and !results.empty?
            results = excerpts(results, client, query)
          end

          results
        end
      end
      alias_method_chain :search, :excerpts

      def strip_bogus_characters(s)
        # Used to remove some garbage before highlighting
        s.gsub(/<.*?>|\.\.\.|\342\200\246|\n|\r/, " ").gsub(/http.*?( |$)/, ' ') if s
      end

      def strip_query_commands(s)
        # XXX Hack for query commands, since Sphinx doesn't intelligently parse the query in excerpt mode
        # Also removes apostrophes in the middle of words so that they don't get split in two.
        s.gsub(/(^|\s)(AND|OR|NOT|\@\w+)(\s|$)/i, "").gsub(/(\w)\'(\w)/, '\1\2')
      end 


    end
  end
end

Simply place the code in a file in the config/initializers folder of your application, and the magic will appear the way it almost always does in Rails.

Update 6/4/09 @ 9:30AM:

1. The excerpt method in Thinking Sphinx wants to examine one of your indices (any one) for character encoding and such. See here. I have used “user_core”. If you don’t have a User model or haven’t defined an index on it, then you will get an “unknown index” error. Simply search the patch for “user_core” and replace it with “#{model_you_have_indexed}_core”.

2. There was an error in the will_paginate version in which the excerpt method was taking the paginated collection and returning a simple array. The patch has been updated so that the excerpt method replaces the results returned by TS with the excerpt highlighted results, without removing the pagination info. My apologies to those who were caught by this problem.

Update 10/06/09

There has been a new version of Thinking Sphinx since I wrote this. I have not had the chance to update this code to match the new plugin. So, if it doesn’t work, that may be why. When I have a chance, I’ll fix it up.

February 26, 2009 in Ruby on Rails

Search Multiple Models at Once with Thinking Sphinx

Buried in the Thinking Sphinx documentation I found a powerfully easy answer to a problem that is otherwise not easy to solve with ActiveRecord. How do you search across multiple models and combine the results into a single set without individually querying each model and melding the results together manually?

One way we have done this is through a union query in a find_by_sql, which works well enough to query multiple tables but only returns results of a single type, unless you refactor ActiveRecord to instantiate different models depending on the type (which we did in this case).

With Thinking Sphinx it’s as simple as this:

ThinkingSphinx::Search.search "term", :classes => [Post, User, Photo, Event]

And the result comes back with a neatly instantiated combination of posts, users, photos, and events.

Is this more efficient than the previous method? I do not know, I will have to check.

February 26, 2009 in Ruby on Rails

Thinking Sphinx with Paginating Find

The Thinking Sphinx plugin comes wired to work directly with will_paginate. However, I use the paginating_find instead.

I placed this block of code in a new file called thinking_sphinx_patch.rb and placed it in my config/initializers folder.

module ThinkingSphinx
  class Search
    class << self
      def search_with_paginating_find(*args)
        query = args.clone  # an array
        options = query.extract_options!

        retry_search_on_stale_index(query, options) do
          results, client = search_results(*(query + [options]))

          ::ActiveRecord::Base.logger.error(
            "Sphinx Error: #{results[:error]}"
          ) if results[:error]

          klass   = options[:class]
          page    = options[:page] ? options[:page].to_i : 1
          total = results[:total]

          results = ThinkingSphinx::Collection.create_from_results(results, page, client.limit, options)

          if options[:page]
            PagingEnumerator.new(client.limit, total, false, page, 1) do |pg|
              results
            end
          else
            results
          end
        end
      end
      alias_method_chain :search, :paginating_find
 
    end
  end
end

The Thinking Sphinx version works by gathering all the results of a query from Sphinx and then passing back a slice that matches your pagination and limit options. To make it work with paginating_find, we needed simply to wrap the results array in a PagingEnumerator while the total number of results is still available.

February 26, 2009 in Ruby on Rails

Forms Within Forms in IE7

Today I learned that you can’t put form tags inside form tags or IE will cause all of the form inputs after the nested form to be disconnected from their parent form.

Well, I already knew that, but was thinking more about adding features quickly than nested forms. Instead of warning me, Firefox worked, and instead of being smart and firing up the virtual PC that is squatting inside my Mac like an underappreciated cousin who overstayed his visit, I published to the live site.

The form in question was the “edit profile” form on the Ithaca Garden Network. The embedded form was the “Me on the Web” block, where you can add links to your profile pages elsewhere on the Web.

The dilemma is that the form wraps the two columns, so whatever lives outside the form must be either above or below the entire two columns of the form.

The solution was to move the Me on the Web block from the bottom of the right column to the bottom of the main column, beneath the form.

February 25, 2009 in CSS, HTML

Disappearing Buttons in IE7

Today I learned that if you use text-indent to hide the text on an input tag that has a background image, IE7 will disappear the whole button and anything that is inline with it. It took 30 minutes to get to that point because one wouldn’t expect such a benign property as text-indent to cause headaches.

There are at least two solutions to the problem: the one that I found and used, and the one that I should have used and might refactor to use.

The first solution is through CSS:

input.submit {
  color:transparent;
  width: 100px; /* a few wider than the background image, which is either transparent or matted to the background color depending on your taste for IE6 */
  font-size:0;
  text-align:right;
}

I chose that solution because I was in a stylesheet, thinking CSS, and it was pretty much outlined at on this blog post.

February 25, 2009 in CSS, HTML

In the Last Year

The past year has proven that I don’t have much time to maintain a blog. The Facebook revolution has made it way easier to push out updates of what is going on, and puts those updates into the hands of those who would care. My hopes of writing a technical blog have dashed upon the shoals of having too many other things to do in the off hours. Among those was rewriting Rose’s Home Dish in Ruby on Rails. I haven’t done any history stuff in the last year, so nothing to write about there.

Our son Dylan was born on September 20. He is now 4 months. You can see pictures on Facebook.

I am still an application developer at Gorges Web Sites. Diana is still the Director of International Student Services at Ithaca College.

February 2, 2009 in Life

Conclusion

Much of the failure and radicalization of the Nazi occupation of Poland can be traced to a series of systemic human factors whose roots like in the world of colonial occupation. I will outline these factors in brief, and then spend the rest of this paper elaborating on this collective “human resources disaster.”

Because We Can.” When holders of a fundamentalist ideology gain control of a colony, they are often tempted to use it as a proving ground for radical policies they are unable to implement at home for reasons of domestic support. When liberals gained control over the East India Company in the 1820s, they used British India as a laboratory for liberal social and economic policies until the rebellion of 1857 signaled their ultimate failure. Likewise, the Bush administration has used Iraq as a terrarium for neoconservative democracy building, especially in the first two years. In Nazi Poland, this was the case in at least two different aspects:

  1. The Government-General became a test bed for “colonial” administrative and economic policies. Methods of agricultural and industrial exploitation and labor extraction were pioneered here with the intention of extending them to the broader East.
  2. The Warthegau and East Upper Silesia (and to a lesser extent, Danzig-West Prussia) became models for the incorporation into the Reich of frontier territories primarily populated by “ethnic aliens” and “enemy peoples.” Here the Nazis tested out new population policies, including resettlement and mass murder, and administrative staffing, at a time when this was still difficult in “the Old Reich.” Included among such policies:
    1. The gradation of the local “German” population into “ethnic” categories based on political reliability (the Deutsche Volksliste).
    2. The ghettoization of Jews and marking them with the Star of David.
    3. The use of forced labor for public works projects.
    4. The resettlement of ethnic Germans from eastern Poland, Romania, the Baltic, and other points east on Polish farmsteads; but perhaps as importantly, their constant cajoling and indoctrination. The Nazi regime could simply go farther with them than with “Reich Germans” because they had nowhere else to go, and were viewed as more primitive outsiders in need of civilizing into “real Germans.”

Help Wanted.” When a radical regime comes to power that claims to represent all of a polity but knows it is only a very strong minority, it acutely senses its weakness and aspires to further control. One means is through staffing policies in occupied territories. For example, the Bush administration in Iraq has become famous for explicitly recruiting ideologically reliable but professionally untrained Republicans instead of more highly qualified candidates. The possible benefits of such a policy are clear:

  1. Increase your party’s hold on power.
  2. Build a personnel base that owes its position to you, and hence will implement your radical, “can’t do it at home” policies without backtalk.

On the reverse side, one increases the risk of not effectively implementing any policy because one’s staff is stocked with hacks and slackers. German administration in the occupied territories all carried this reputation, especially in the East.

“Colonialism isn’t for everybody.” It takes a special kind of person to succeed in colonialism’s unusual environment. A member of the “master race” needs to have social confidence, cultural sensitivity if not respect for the host culture, and a strong base of moral values to guide one’s exercise of near-dictatorial power over subject populations. Willpower alone is not enough. Thus the British colonial administration recruited heavily among the lower upper middle classes. These were low enough to view “the colonies” as an honorable career, but high enough to not let it get to their heads when dealing with “native” chiefs and rajas. Their public school upbringing gave them a moral compass that was immature, cruel, and lacking in many respects, but generally gave them the fortitude not to lose their heads in the jungle. This did not hinder racial arrogance, nor did it prevent them from the committing atrocities and from pursuing policies that were overall cruel in the belief that they were benefiting humanity.

In Imperial Life in the Emerald City, Rajiv Chandrasekaran argues that much of US policy in Iraq has been hindered by essentially human resources factors. Alongside recruiting based on ideology rather than experience, he argues that the Americans charged with rebuilding Iraq were simply not prepared for the cultural encounter they were about to undergo. Most had never left the US, and were not trained for cultural encounter so much as for desert warfare. It pleased me to see this book because its author makes the same argument I have been making for years about Nazi personnel policy (if in less eloquent form):

If one takes several thousand bookkeepers, shopkeepers, carpenters, and gardeners whose chief distinction is their dedication since the late 1920s and early 1930s to a movement that rejects key civic virtues in favor of Herrenmensch ideology and a “get it done” attitude, mainly lower middle class men who have never left Germany except in uniform, and give them power …

Married men with children, getting good salaries for doing their best but owing their pay to their political service, in difficult conditions, with minimum orientation, with little social support beyond admonitions to “be a man” …

The result will be a disaster.

January 12, 2008 in History

Perpetrators?

The previous arguments can be rounded out and compared to Mann’s study by the addition of a few numbers. The first area is region, for me the most fascinating. Mann had found in his sample that southern Germans and Austrians – as well as Catholics overall – were strongly over-represented, whereas the northern and central German regions that had given Hitler the most electoral support in 1933 were strongly under-represented. Out of this he builds a case that the animus of the Nazi movement had shifted from kleindeutsch to grossdeutsch interests, with corresponding regional recruiting tendencies. The finding is intriguing, the explanation unconvincing:

  1. The SS was the “prime mover” agency of Holocaust perpetrators. The “Black Corps” was southern German before it became national.

  2. Heavy among the early recruits were refugees from the “lost territories” of World War I. With the (partial) exception of Prussian Poland, Germans from these areas were largely Catholic.

  3. The kernel of the RSHA began with Eichmann’s office in Vienna in November 1938. As this office expanded and moved its base of operations, it retained at least some of its original Austrian flavor.

Figure 1: Representation of German Regions Among Officials in the Warthegau. How to read: the darker the color, the more numerous the Germans from that region.

First, a bookkeeping question: where do the colors and numbers come from? Using the population statistics in the 1941 Reich Statistical Yearbook, I calculated the percentage of total German population for each German state and Prussian Land. I placed each of the Germans in my sample born in those borders into the appropriate category. Then I ran the numbers for the Government General, RKU, RKO, and so forth, as well as for quite a few combinations in this sample. For each Land, I used the population ratio to determine how many from there should have been in the sample, and divided it into the number actually there. The result was a ratio in which 1.5 meant there were half again as many from a place as there should have been. Finally I thought 36 categories a bit overwhelming, so I grouped the numbers into logical regions so as to minimize distortions. Finally, I admitted that all my lists of“regional representation ratios” were no more enlightening than this one map, so I present the map by itself.

The map shows a dearth of southern Germans wanting to build Greater Germany in the Litzmannstadt region. Some observations on why:

  1. Prussian Poland was a Prussian obsession. However much they were building Grossdeutschland in the Warthegau, it was still a revived Provinz Posen. Hence the preponderance of Prussians.
  2. Familiarity and similarity to Poland was a huge stimulus; the ability to speak Polish without actually being Polish even greater. Upper Silesians especially fall into this category.
  3. Distance was important, combined with being on the Eastern frontier and hence more likely to be concerned with “the Eastern Question.” Lower Silesia and Saxony fall into this category, along with Brandenburg, Pomerania, and East Prussia. Similarity of topography is also important here. If one wants to find Austrians, Bavarians, and Badensians in the majority, one should look in Alsace-Lorraine, Oberkrain, and Untersteiermark.
  4. Prussia and Saxony were more administratively Nazified by 1939 than Baden, Bavaria, and Württemberg. When the call for Nazi administrators went out in 1940, there were simply more north Germans than south Germans qualified to sign up. This at best was a minor factor.

The analytical knife can be sharpened a bit by adding in Partymembership. Where did the Old Fighters come from? Where did the nonmembers come from? That map is identical to Figure 1 with one main difference: Upper Silesians and East Prussians were slightly less likely than everybody else to be NSDAP members. Saxony and Lower Silesia took up the slack. This suggests to me that cultural proximity to Poland was important for Upper Silesia and East Prussia, whereas frontier mentality – the desire to be a Kolonialpionier – was more important in Saxony and Lower Silesia.

Poland is a Catholic country. What role did religion play? Were Catholics less likely to be in the Warthegau because Poland was a Catholic country? In the Eastern Marches the German colonizers were historically associated with Protestantism, Poles with Catholicism. 80 percent of my Lodz sample was Protestant; minus the Saxons, most of these were Prussians. Among the Catholics there was a clear division between the Amtskommissare and everybody else. Three-quarters of Catholic Amtskommissare had declared themselves “formerly” Catholic gottgläubige. Only one-quarter of the other Catholics had done the same. The difference among Protestants was not so drastic, more like 53 percent gottgläubige Amtskommissare, 38 percent everybody else. I conclude that the Amtskommissar comprised the lowest level of the ideological leadership corps – and hence spent more time on them – than the other men. The dedicated were more likely to have left the Catholic Church than the Evangelical possibly because Rome exerted a greater claim to temporal authority, whereas the Lutheran pastors were more likely to preach Nazism from the pulpit. In any case, it is unlikely that Catholics avoided the Warthegaubecause they were Catholic. Neither southern Germany nor Austria was over-represented in the Government-General, which is no less Catholic than the Warthegau.

The group appears rather spiesserlich to me. Because of this, I used to draw parallels to the quintessential American Spiessertyp, Sinclair Lewis’ Babbitt. Babbitt got his morals from the newspaper, was pro-business and anti-socialist without any thought, thought his hometown was the best, worked in his office every day, and strove to move up in his city’s society. I tended to imagine the Litzmannstädter in the same mould. After six years of having to look interested in all of the rah-rah Party activism, they could hide in some backwoods Polish town and do nothing but drink and trade on the black market. If they had to comply with some uncomfortable decrees in order to maintain this comfortable state, why not? Yet most of these stereotypes arose in the Government-General and the Reichskommissariate. Although the Warthegau surely saw its share of misfits and ruffians, I now tend toward the opposite view: most of these men lived the ideology, were caught up in the pioneer spirit of Aufbauarbeit, and enjoyed career rewards as a result of their dedication.

January 12, 2008 in History