Making design doc thread safe for checking status of design doc

This commit is contained in:
Sam Lown 2010-04-06 17:51:17 +00:00
parent 77cb8160c7
commit 5fd1c3903b
7 changed files with 43 additions and 57 deletions

View file

@ -336,7 +336,7 @@ module CouchRest
end end
def clear_extended_doc_fresh_cache def clear_extended_doc_fresh_cache
::CouchRest::ExtendedDocument.subclasses.each{|klass| klass.design_doc_fresh = false if klass.respond_to?(:design_doc_fresh=) } ::CouchRest::ExtendedDocument.subclasses.each{|klass| klass.req_design_doc_refresh if klass.respond_to?(:req_design_doc_refresh)}
end end
def uri_for_attachment(doc, name) def uri_for_attachment(doc, name)

View file

@ -107,16 +107,12 @@ module CouchRest
@klass.design_doc @klass.design_doc
end end
def design_doc_fresh
@klass.design_doc_fresh
end
def refresh_design_doc def refresh_design_doc
@klass.refresh_design_doc_on(@database) @klass.refresh_design_doc(@database)
end end
def save_design_doc def save_design_doc
@klass.save_design_doc_on(@database) @klass.save_design_doc(@database)
end end
end end
end end

View file

@ -9,23 +9,23 @@ module CouchRest
end end
module ClassMethods module ClassMethods
attr_accessor :design_doc, :design_doc_slug_cache, :design_doc_fresh
def design_doc def design_doc
@design_doc ||= Design.new(default_design_doc) @design_doc ||= Design.new(default_design_doc)
end end
# Use when something has been changed, like a view, so that on the next request
# the design docs will be updated.
def req_design_doc_refresh
@design_doc_fresh = { }
end
def design_doc_id def design_doc_id
"_design/#{design_doc_slug}" "_design/#{design_doc_slug}"
end end
def design_doc_slug def design_doc_slug
return design_doc_slug_cache if (design_doc_slug_cache && design_doc_fresh) self.to_s
funcs = []
design_doc['views'].each do |name, view|
funcs << "#{name}/#{view['map']}#{view['reduce']}"
end
self.design_doc_slug_cache = self.to_s
end end
def default_design_doc def default_design_doc
@ -43,45 +43,44 @@ module CouchRest
} }
end end
def refresh_design_doc def refresh_design_doc(db = database)
reset_design_doc unless design_doc_fresh(db)
save_design_doc reset_design_doc(db)
end save_design_doc(db)
end
def refresh_design_doc_on(db)
reset_design_doc
save_design_doc_on(db)
end
# Save the design doc onto the default database, and update the
# design_doc attribute
def save_design_doc
reset_design_doc unless design_doc_fresh
self.design_doc = update_design_doc(design_doc)
end end
# Save the design doc onto a target database in a thread-safe way, # Save the design doc onto a target database in a thread-safe way,
# not modifying the model's design_doc # not modifying the model's design_doc
def save_design_doc_on(db) def save_design_doc(db = database)
update_design_doc(Design.new(design_doc), db) update_design_doc(Design.new(design_doc), db)
end end
private protected
def reset_design_doc def design_doc_fresh(db, fresh = nil)
current = self.database.get(design_doc_id) rescue nil @design_doc_fresh ||= {}
if fresh.nil?
@design_doc_fresh[db.uri] || false
else
@design_doc_fresh[db.uri] = fresh
end
end
def reset_design_doc(db)
current = db.get(design_doc_id) rescue nil
design_doc['_id'] = design_doc_id design_doc['_id'] = design_doc_id
if current.nil? if current.nil?
design_doc.delete('_rev') design_doc.delete('_rev')
else else
design_doc['_rev'] = current['_rev'] design_doc['_rev'] = current['_rev']
end end
self.design_doc_fresh = true design_doc_fresh(db, true)
end end
# Writes out a design_doc to a given database, returning the # Writes out a design_doc to a given database, returning the
# updated design doc # updated design doc
def update_design_doc(design_doc, db = database) def update_design_doc(design_doc, db)
saved = db.get(design_doc['_id']) rescue nil saved = db.get(design_doc['_id']) rescue nil
if saved if saved
design_doc['views'].each do |name, view| design_doc['views'].each do |name, view|
@ -100,4 +99,4 @@ module CouchRest
end end
end end
end end

View file

@ -83,8 +83,8 @@ module CouchRest
opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')" opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')"
end end
keys.push opts keys.push opts
self.design_doc.view_by(*keys) design_doc.view_by(*keys)
self.design_doc_fresh = false req_design_doc_refresh
end end
# returns stored defaults if the there is a view named this in the design doc # returns stored defaults if the there is a view named this in the design doc
@ -96,9 +96,7 @@ module CouchRest
# Dispatches to any named view. # Dispatches to any named view.
def view(name, query={}, &block) def view(name, query={}, &block)
db = query.delete(:database) || database db = query.delete(:database) || database
unless design_doc_fresh refresh_design_doc(db)
refresh_design_doc_on(db)
end
query[:raw] = true if query[:reduce] query[:raw] = true if query[:reduce]
raw = query.delete(:raw) raw = query.delete(:raw)
fetch_view_with_docs(db, name, query, raw, &block) fetch_view_with_docs(db, name, query, raw, &block)
@ -119,13 +117,6 @@ module CouchRest
end end
end end
# Deletes the current design doc for the current class.
# Running it to early could mean that live code has to regenerate
# potentially large indexes.
def cleanup_design_docs!(db = database)
save_design_doc_on(db)
end
private private
def fetch_view_with_docs(db, name, opts, raw=false, &block) def fetch_view_with_docs(db, name, opts, raw=false, &block)
@ -156,7 +147,7 @@ module CouchRest
# the design doc may not have been saved yet on this database # the design doc may not have been saved yet on this database
rescue HttpAbstraction::ResourceNotFound => e rescue HttpAbstraction::ResourceNotFound => e
if retryable if retryable
save_design_doc_on(db) save_design_doc(db)
retryable = false retryable = false
retry retry
else else

View file

@ -327,7 +327,7 @@ describe "ExtendedDocument" do
describe "finding all instances of a model" do describe "finding all instances of a model" do
before(:all) do before(:all) do
WithTemplateAndUniqueID.design_doc_fresh = false WithTemplateAndUniqueID.req_design_doc_refresh
WithTemplateAndUniqueID.all.map{|o| o.destroy(true)} WithTemplateAndUniqueID.all.map{|o| o.destroy(true)}
WithTemplateAndUniqueID.database.bulk_delete WithTemplateAndUniqueID.database.bulk_delete
WithTemplateAndUniqueID.new('important-field' => '1').save WithTemplateAndUniqueID.new('important-field' => '1').save
@ -349,7 +349,7 @@ describe "ExtendedDocument" do
describe "counting all instances of a model" do describe "counting all instances of a model" do
before(:each) do before(:each) do
@db = reset_test_db! @db = reset_test_db!
WithTemplateAndUniqueID.design_doc_fresh = false WithTemplateAndUniqueID.req_design_doc_refresh
end end
it ".count should return 0 if there are no docuemtns" do it ".count should return 0 if there are no docuemtns" do
@ -368,7 +368,7 @@ describe "ExtendedDocument" do
describe "finding the first instance of a model" do describe "finding the first instance of a model" do
before(:each) do before(:each) do
@db = reset_test_db! @db = reset_test_db!
WithTemplateAndUniqueID.design_doc_fresh = false WithTemplateAndUniqueID.req_design_doc_refresh
WithTemplateAndUniqueID.new('important-field' => '1').save WithTemplateAndUniqueID.new('important-field' => '1').save
WithTemplateAndUniqueID.new('important-field' => '2').save WithTemplateAndUniqueID.new('important-field' => '2').save
WithTemplateAndUniqueID.new('important-field' => '3').save WithTemplateAndUniqueID.new('important-field' => '3').save

View file

@ -76,9 +76,9 @@ describe "Subclassing an ExtendedDocument" do
it "should have its own design_doc_fresh" do it "should have its own design_doc_fresh" do
Animal.refresh_design_doc Animal.refresh_design_doc
Dog.design_doc_fresh.should_not == true Dog.send(:design_doc_fresh, Dog.database).should_not == true
Dog.refresh_design_doc Dog.refresh_design_doc
Dog.design_doc_fresh.should == true Dog.send(:design_doc_fresh, Dog.database).should == true
end end
it "should not add views to the parent's design_doc" do it "should not add views to the parent's design_doc" do

View file

@ -126,7 +126,7 @@ describe "ExtendedDocument views" do
lambda{Unattached.all}.should raise_error lambda{Unattached.all}.should raise_error
end end
it "should query all" do it "should query all" do
Unattached.cleanup_design_docs!(@db) # Unattached.cleanup_design_docs!(@db)
rs = Unattached.all :database => @db rs = Unattached.all :database => @db
rs.length.should == 4 rs.length.should == 4
end end
@ -187,7 +187,7 @@ describe "ExtendedDocument views" do
Unattached.view_by :questions Unattached.view_by :questions
Unattached.by_questions(:database => @db) Unattached.by_questions(:database => @db)
original_revision = Unattached.model_design_doc(@db)['_rev'] original_revision = Unattached.model_design_doc(@db)['_rev']
Unattached.cleanup_design_docs!(@db) Unattached.save_design_doc(@db)
Unattached.model_design_doc(@db)['_rev'].should_not == original_revision Unattached.model_design_doc(@db)['_rev'].should_not == original_revision
end end
end end