new_file allows optional :info hash to be populated
authorEric Wong <normalperson@yhbt.net>
Mon, 22 Oct 2012 20:18:06 +0000 (22 20:18 +0000)
committerEric Wong <normalperson@yhbt.net>
Mon, 22 Oct 2012 21:02:43 +0000 (22 21:02 +0000)
This allows clients to avoid calling #file_info or #get_uris
immediate after uploading a file to MogileFS.  This can speed
things up for cache-using clients with write-through caching.

lib/mogilefs/mogilefs.rb
lib/mogilefs/new_file/common.rb
test/test_fresh.rb

index f5cb096..76325cb 100644 (file)
@@ -244,6 +244,13 @@ class MogileFS::MogileFS < MogileFS::Client
   #   Keep in mind most HTTP servers do not support HTTP trailers, so
   #   passing a String is usually the safest way to use this.
   #
+  # [:info => Hash]
+  #
+  #   This is an empty hash that will be filled the same information
+  #   MogileFS::MogileFS#file_info.
+  #
+  #   Additionally, it contains one additional key: :uris,
+  #   an array of URI::HTTP objects to the stored destinations
   def new_file(key, args = nil, bytes = nil) # :yields: file
     raise MogileFS::ReadOnlyError if readonly?
     opts = { :key => key, :multi_dest => 1 }
@@ -275,6 +282,7 @@ class MogileFS::MogileFS < MogileFS::Client
     opts[:content_length] ||= bytes if bytes
     opts[:new_file_max_time] ||= @new_file_max_time
     opts[:start_time] = Time.now
+    info = opts[:info] and info["class"] = klass || "default"
 
     case (dests[0][1] rescue nil)
     when %r{\Ahttp://}
index e959a21..9b6511d 100644 (file)
@@ -34,21 +34,21 @@ module MogileFS::NewFile::Common
   end
 
   def create_close(devid, uri, bytes_uploaded)
-    args = {
-      :fid => @opts[:fid],
-      :devid => devid,
-      :key => @opts[:key],
-      :domain => @opts[:domain],
-      :size => bytes_uploaded,
-      :path => uri.to_s,
-    }
+    dest_info = @opts[:info] ||= {}
+    dest_info["fid"] = @opts[:fid].to_i
+    dest_info["key"] = @opts[:key]
+    dest_info["domain"] = @opts[:domain]
+    dest_info[:devid] = devid
+    dest_info[:path] = uri.to_s
+    dest_info[:size] = bytes_uploaded
     if @md5
-      args[:checksum] = "MD5:#{@md5.hexdigest}"
+      dest_info["checksum"] = "MD5:#{@md5.hexdigest}"
     elsif String === @opts[:content_md5]
       hex = @opts[:content_md5].unpack('m')[0].unpack('H*')[0]
-      args[:checksum] = "MD5:#{hex}"
+      dest_info["checksum"] = "MD5:#{hex}"
     end
-    args[:checksumverify] = 1 if @opts[:checksumverify]
+
+    dest_info[:checksumverify] = 1 if @opts[:checksumverify]
     backend = @opts[:backend]
 
     # upload could've taken a long time, ping and try to ensure socket
@@ -61,7 +61,16 @@ module MogileFS::NewFile::Common
     # twice will hurt us...
     backend.noop
 
-    backend.create_close(args)
+    backend.create_close(dest_info)
+
+    # make this look like file_info + get_uris
+    dest_info.delete(:checksumverify)
+    dest_info.delete(:path)
+    dest_info[:uris] = [ uri ]
+    dest_info["devcount"] = 1
+    dest_info["devids"] = [ dest_info.delete(:devid).to_i ]
+    dest_info["length"] = dest_info.delete(:size)
+
     bytes_uploaded
   end
 
index f20aacc..369e363 100644 (file)
@@ -32,4 +32,62 @@ class TestMogFresh < Test::Unit::TestCase
     client = MogileFS::MogileFS.new :hosts => @hosts, :domain => @domain
     assert_equal false, client.exist?("non-existent")
   end
+
+  def test_new_file_info(checksum = nil)
+    add_host_device_domain unless checksum
+    @client = MogileFS::MogileFS.new :hosts => @hosts, :domain => @domain
+    info = {}
+    opts = { :info => info }
+    key = "new_file_info"
+    content = "ZZZZ"
+    if checksum
+      opts[:content_md5] = [ Digest::MD5.digest(content) ].pack('m').rstrip
+      opts[:class] = "check"
+    end
+    rv = @client.new_file(key, opts) do |http_file|
+      http_file << content
+    end
+
+    uris = info.delete(:uris)
+    assert_kind_of Array, uris
+    assert_equal(uris, (@client.get_uris(key) & uris))
+    expect_info = @client.file_info(key, :devices => true)
+    match_keys = %w(class fid key domain length)
+    match_keys << "checksum" if checksum
+    match_keys.each do |field|
+      assert_equal expect_info.delete(field), info.delete(field)
+    end
+    assert_operator expect_info.delete("devcount"), :>=, info.delete("devcount")
+    devids = info.delete("devids")
+    assert_equal(devids, (expect_info.delete("devids") & devids))
+
+    assert info.empty?, info.inspect
+    assert expect_info.empty?, expect_info.inspect
+  ensure
+    @client.delete(key)
+  end
+
+  def test_new_file_info_checksum
+    add_host_device_domain
+    opts = @admin.get_domains[@domain]["default"]
+    opts["hashtype"] = "MD5"
+    @admin.create_class(@domain, "check", opts)
+    yield_for_monitor_update do
+      tmp = @admin.get_domains[@domain]["check"]
+      if tmp
+        case tmp["hashtype"]
+        when "MD5"
+          break
+        when nil
+          warn "skipping checksum test, MogileFS server too old"
+          return
+        else
+          raise "Unhandled hashtype: #{tmp['hashtype']}"
+        end
+      end
+    end
+    test_new_file_info(:md5)
+  ensure
+    @admin.delete_class @domain, "check"
+  end
 end