# # rename_file "fs_http.py" # to "fs_read_httpftp.py" # # patch "TODO" # from [4c1f53a98bd124401e02f0009de48a780b77b2b5] # to [941317342c321076783889e4424f2a4007c7459a] # # patch "dumb.py" # from [532954757a0191f36cdaf12f96d50425aa6afeba] # to [d10d1c98a57ed51b38b7649cd09c583de05ec2c4] # # patch "fs.py" # from [28b941965155288861b38b1c478e5993a7a22cb7] # to [0741851af22991c3a1faf7345c8b85ab3b4c5a9f] # # patch "fs_read_httpftp.py" # from [d5d3296ad125d00865d83cce4ea508802ed228b9] # to [4195f2926cfb7791501f7bb587cb9b672775263b] # # patch "merkle_dir.py" # from [9c2532dc27fc69184efb6d30fca499dfa23481f3] # to [cb2d117a188c3ce2637e0883705202779265600a] # ======================================================================== --- TODO 4c1f53a98bd124401e02f0009de48a780b77b2b5 +++ TODO 941317342c321076783889e4424f2a4007c7459a @@ -1,10 +1,14 @@ -* packet commands -> automate? +* packet commands -> automate? (combines with next item:) +* use automate stdio interface (and automate get_revision) to make + export much faster * merkle dir stuff that doesn't require loading entire chunks into memory all the time -* ftp/sftp/http - - with pipelining +* ftp write + - probably refactor SFTP file write stuff +* pipelining on http read? (is urlgrabber thread-safe?) * possibly better rollback stuff? - - e.g., truncate when possible + - truncate DATA when possible? (local, sftp (with a simple call + to a private method) - include some info in the lockdir on who has things locked, to aid in detecting staleness of locks * file reader needs to do the pause/retry thing on missing files ======================================================================== --- dumb.py 532954757a0191f36cdaf12f96d50425aa6afeba +++ dumb.py d10d1c98a57ed51b38b7649cd09c583de05ec2c4 @@ -100,6 +100,7 @@ print "Pulling changes from %s to %s" % (source_url, local_url) local_md = MerkleDir(writeable_fs_for_url(local_url)) source_md = MerkleDir(readable_fs_for_url(source_url)) + monotone.ensure_db() feeder = monotone.feeder() fc = FeederCallback(feeder) local_md.pull(source_md, fc) ======================================================================== --- fs.py 28b941965155288861b38b1c478e5993a7a22cb7 +++ fs.py 0741851af22991c3a1faf7345c8b85ab3b4c5a9f @@ -16,8 +16,8 @@ assert not host return LocalReadableFS(path) elif scheme in ("http", "https", "ftp"): - import fs_http - return fs_http.HTTPReadableFS(url) + import fs_read_httpftp + return fs_read_httpftp.HTTPFTPReadableFS(url) elif scheme == "sftp": import fs_sftp return fs_sftp.SFTPReadableFS(host, path) ======================================================================== --- fs_read_httpftp.py d5d3296ad125d00865d83cce4ea508802ed228b9 +++ fs_read_httpftp.py 4195f2926cfb7791501f7bb587cb9b672775263b @@ -1,11 +1,15 @@ # we need urlgrabber to do range fetches import fs import urlparse import urlgrabber +import urlgrabber.grabber -class HTTPReadableFS(fs.ReadableFS): +class HTTPFTPReadableFS(fs.ReadableFS): def __init__(self, url): self.url = url + assert self.url + if self.url[-1] != "/": + self.url += "/" def _url(self, filename): return urlparse.urljoin(self.url, filename) @@ -18,12 +22,14 @@ for fn in filenames: try: files[fn] = urlgrabber.urlread(self._url(fn)) - except urlgrabber.URLGrabError: + except urlgrabber.grabber.URLGrabError: files[fn] = None return files def _real_fetch_bytes(self, filename, bytes): url = self._url(filename) for offset, length in bytes: - yield urlgrabber.urlread(url, range=(offset, offset+length)) + # for HTTP, this is actually somewhat inefficient + yield ((offset, length), + urlgrabber.urlread(url, range=(offset, offset+length))) ======================================================================== --- merkle_dir.py 9c2532dc27fc69184efb6d30fca499dfa23481f3 +++ merkle_dir.py cb2d117a188c3ce2637e0883705202779265600a @@ -67,10 +67,7 @@ # a client may receive only A, or only B. # # In some situations (FTP, NTFS, ...) it is actually impossible to -# atomically replace a file. In these cases we simply bite the bullet -# and have a very small race condition, while each file is being -# swapped around. If readers try to open a file but find it does not -# exist, they should try again after a short pause, before giving up. +# atomically replace a file. See below. # -- atomically replace the root hash file (subject again to the above # proviso) # -- remove the lockdir @@ -78,7 +75,19 @@ # If a connection is interrupted uncleanly, or there is a stale lock, we: # -- check for any missing files left by non-atomic renames # -- remove the lockdir +# +# Atomic update: +# -- We use atomic updates when we can. When we can't, we have +# systematic handling, to make sure that if a, say, session using SFTP +# is interrupted, it can be rolled back over, say, the local-fs +# transport. +# -- +# In these cases we simply bite the bullet +# and have a very small race condition, while each file is being +# swapped around. If readers try to open a file but find it does not +# exist, they should try again after a short pause, before giving up. + class _HashFile: prefix = "" values = ()