diff --git a/README.md b/README.md index 744dcbf..4b59fcc 100755 --- a/README.md +++ b/README.md @@ -237,6 +237,20 @@ zookeeper::zsync $zk $path $zpath ?pattern? Sync a filesystem tree to a znode tree. zpath is prepended to the destination path. Compares existing files and znode data and if they are present and identical, does not update the znode. This makes znode versions increment only when changes are present in corresponding files when zsync is run. +```tcl +zookeeper::sync_ztree_to_directory $zk $zpath $path +``` + +Recursively copy a zookeeper tree to a directory in a filesystem. + +All nodes will be created as directories. + +If a znode contains data the data will be written in the corresponding directory as the file _zdata_ and the version as the file _zversion_. + +The function makes the effort to skip rewriting the data and version files if the existing data and the version files are the same. (This considerably speeds up the function and reduces filesystem churn.) + +Also in the event of changes if there was already a zdata and zversion to renames new files in rather than overwriting them so a reader reading the file at an inopportune moment won't get an empty file. + Errata --- diff --git a/configure.in b/configure.in index a4fc12c..1063669 100755 --- a/configure.in +++ b/configure.in @@ -18,7 +18,7 @@ dnl to configure the system for the local environment. # so you can encode the package version directly into the source files. #----------------------------------------------------------------------- -AC_INIT([zookeeper], [0.0]) +AC_INIT([zookeeper], [1.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. diff --git a/zookeeper.tcl b/zookeeper.tcl index 32ba0fb..f6609ab 100644 --- a/zookeeper.tcl +++ b/zookeeper.tcl @@ -4,6 +4,7 @@ # namespace eval ::zookeeper { + variable zkwd "/" # # mkpath - make all the znodes @@ -31,6 +32,12 @@ namespace eval ::zookeeper { return $data } + proc write_file {file data} { + set fp [open $file w] + puts -nonewline $fp $data + close $fp + } + # # copy_file - copy a file # @@ -87,21 +94,84 @@ namespace eval ::zookeeper { } # - # zcopy - copy a znode tree to a filesystem + # sync_ztree_to_filetree - copy a znode tree to a filesystem # # zpath is prepended to the destination path # - proc zcopy {zk zpath path {pattern *}} { - file mkdir $path - set regexp "^${path}(.*)" + proc sync_ztree_to_directory {zk zpath path} { + #puts stderr "sync_ztree_to_directory $zk $zpath $path" + set outpath $path/$zpath + file mkdir $outpath + if {![$zk get $zpath -data zdata -version zversion]} { + file delete $outpath/zdata $outpath/zversion + } else { + set write 1 + set exists 0 + try { + file stat $outpath/zdata stat + if {$stat(size) == [string length $zdata] && [read_file $outpath/zdata] eq $zdata && [read_file $outpath/zversion] eq $zversion} { + #puts stderr "skip $zpath - data matches" + set write 0 + } + } trap {POSIX ENOENT} {} { + set exists 1 + } + + if {$write} { + if {$exists} { + # we need to write and the files exist, do it more cleanly + # so a reader won't see an empty file if they look at just + # the right time. + # NB still a race condition here where the zdata is updated + # but very briefly the zversion isn't. + write_file $outpath/zdata.new $zdata + write_file $outpath/zversion.new $zversion + file rename -force -- $outpath/zdata.new $outpath/$zdata + file rename -force -- $outpath/zversion.new $outpath/$zversion + } else { + # files didn't exist, we can write them without renaming + write_file $outpath/zdata $zdata + write_file $outpath/zversion $zversion + } + } + } set children [$zk children $zpath] foreach znode $children { - regexp $regexp $file dummy tail - copy_file $zk $file $zpath$tail - regexp $regexp $dir dummy tailDir - zcopy $zk $dir $zpath$tailDir $pattern + sync_ztree_to_directory $zk [file join $zpath $znode] $path } } + + proc flatten_path {zpath} { + # take out .. and whatever preceded it and anchor to / + } + + proc zls {{where ""}} { + variable zkwd + + set children [zk children [file join $zkwd $where]] + return [lsort $children] + } + + proc zpwd {} { + variable zkwd + + return $zkwd + } + + proc zcd {{where ""}} { + variable zkwd + + if {$where eq ""} { + set zkwd / + } else { + set children + } + return + } + + proc zcat {{what ""}} { + variable zkwd + } } ;# namespace ::zookeeper # vim: set ts=4 sw=4 sts=4 noet :