LogCabin
|
00001 /* Copyright (c) 2012 Stanford University 00002 * Copyright (c) 2014 Diego Ongaro 00003 * 00004 * Permission to use, copy, modify, and distribute this software for any 00005 * purpose with or without fee is hereby granted, provided that the above 00006 * copyright notice and this permission notice appear in all copies. 00007 * 00008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00015 */ 00016 00017 #include <map> 00018 #include <string> 00019 #include <vector> 00020 00021 #include "Core/ProtoBuf.h" 00022 00023 #ifndef LOGCABIN_TREE_TREE_H 00024 #define LOGCABIN_TREE_TREE_H 00025 00026 namespace LogCabin { 00027 00028 // forward declaration 00029 namespace Protocol { 00030 class ServerStats_Tree; 00031 } 00032 00033 namespace Tree { 00034 00035 /** 00036 * Status codes returned by Tree operations. 00037 */ 00038 enum class Status { 00039 00040 /** 00041 * The operation completed successfully. 00042 */ 00043 OK = 0, 00044 00045 /** 00046 * If an argument is malformed (for example, a path that does not start 00047 * with a slash). 00048 */ 00049 INVALID_ARGUMENT = 1, 00050 00051 /** 00052 * If a file or directory that is required for the operation does not 00053 * exist. 00054 */ 00055 LOOKUP_ERROR = 2, 00056 00057 /** 00058 * If a directory exists where a file is required or a file exists where 00059 * a directory is required. 00060 */ 00061 TYPE_ERROR = 3, 00062 00063 /** 00064 * A predicate on an operation was not satisfied. 00065 */ 00066 CONDITION_NOT_MET = 4, 00067 }; 00068 00069 /** 00070 * Print a status code to a stream. 00071 */ 00072 std::ostream& 00073 operator<<(std::ostream& os, Status status); 00074 00075 /** 00076 * Returned by Tree operations; contain a status code and an error message. 00077 */ 00078 struct Result { 00079 /** 00080 * Default constructor. Sets status to OK and error to the empty string. 00081 */ 00082 Result(); 00083 /** 00084 * A code for whether an operation succeeded or why it did not. This is 00085 * meant to be used programmatically. 00086 */ 00087 Status status; 00088 /** 00089 * If status is not OK, this is a human-readable message describing what 00090 * went wrong. 00091 */ 00092 std::string error; 00093 }; 00094 00095 namespace Internal { 00096 00097 /** 00098 * A leaf object in the Tree; stores an opaque blob of data. 00099 */ 00100 class File { 00101 public: 00102 /// Default constructor. 00103 File(); 00104 /** 00105 * Write the file to the stream. 00106 */ 00107 void dumpSnapshot(Core::ProtoBuf::OutputStream& stream) const; 00108 /** 00109 * Load the file from the stream. 00110 */ 00111 void loadSnapshot(Core::ProtoBuf::InputStream& stream); 00112 /** 00113 * Opaque data stored in the File. 00114 */ 00115 std::string contents; 00116 }; 00117 00118 /** 00119 * An interior object in the Tree; stores other Directories and Files. 00120 * Pointers returned by this class are valid until the File or Directory they 00121 * refer to is removed. 00122 */ 00123 class Directory { 00124 public: 00125 /// Default constructor. 00126 Directory(); 00127 00128 /** 00129 * List the contents of the directory. 00130 * \return 00131 * The names of the directories and files that this directory 00132 * immediately contains. The names of directories in this listing will 00133 * have a trailing slash. The order is first directories (sorted 00134 * lexicographically), then files (sorted lexicographically). 00135 */ 00136 std::vector<std::string> getChildren() const; 00137 00138 /** 00139 * Find the child directory by the given name. 00140 * \param name 00141 * Must not contain a trailing slash. 00142 * \return 00143 * The directory by the given name, or 00144 * NULL if it is not found or a file exists by that name. 00145 */ 00146 Directory* lookupDirectory(const std::string& name); 00147 /** 00148 * Find the child directory by the given name (const version). 00149 * \copydetails lookupDirectory 00150 */ 00151 const Directory* lookupDirectory(const std::string& name) const; 00152 /** 00153 * Find the child directory by the given name, or create it if it doesn't 00154 * exist. 00155 * \param name 00156 * Must not contain a trailing slash. 00157 * \return 00158 * The directory by the given name, or 00159 * NULL if a file exists by that name. 00160 */ 00161 Directory* makeDirectory(const std::string& name); 00162 /** 00163 * Remove the child directory by the given name, if any. This will remove 00164 * all the contents of the directory as well. 00165 * \param name 00166 * Must not contain a trailing slash. 00167 */ 00168 void removeDirectory(const std::string& name); 00169 00170 /** 00171 * Find the child file by the given name. 00172 * \param name 00173 * Must not contain a trailing slash. 00174 * \return 00175 * The file by the given name, or 00176 * NULL if it is not found or a directory exists by that name. 00177 */ 00178 File* lookupFile(const std::string& name); 00179 /** 00180 * Find the child file by the given name (const version). 00181 * \copydetails lookupFile 00182 */ 00183 const File* lookupFile(const std::string& name) const; 00184 /** 00185 * Find the child file by the given name, or create it if it doesn't exist. 00186 * \param name 00187 * Must not contain a trailing slash. 00188 * \return 00189 * The file by the given name, or 00190 * NULL if a directory exists by that name. 00191 */ 00192 File* makeFile(const std::string& name); 00193 /** 00194 * Remove the child file by the given name, if any. 00195 * \param name 00196 * Must not contain a trailing slash. 00197 * \return 00198 * True if child file removed, false if no such file existed. This is 00199 * mostly useful for counting statistics. 00200 */ 00201 bool removeFile(const std::string& name); 00202 00203 /** 00204 * Write the directory and its children to the stream. 00205 */ 00206 void dumpSnapshot(Core::ProtoBuf::OutputStream& stream) const; 00207 /** 00208 * Load the directory and its children from the stream. 00209 */ 00210 void loadSnapshot(Core::ProtoBuf::InputStream& stream); 00211 00212 private: 00213 /** 00214 * Map from names of child directories (without trailing slashes) to the 00215 * Directory objects. 00216 */ 00217 std::map<std::string, Directory> directories; 00218 /** 00219 * Map from names of child files to the File objects. 00220 */ 00221 std::map<std::string, File> files; 00222 }; 00223 00224 /** 00225 * This is used by Tree to parse symbolic paths into their components. 00226 */ 00227 class Path { 00228 public: 00229 /** 00230 * Constructor. 00231 * \param symbolic 00232 * A path delimited by slashes. This must begin with a slash. 00233 * (It should not include "/root" to arrive at the root directory.) 00234 * \warning 00235 * The caller must check "result" to see if the path was parsed 00236 * successfully. 00237 */ 00238 explicit Path(const std::string& symbolic); 00239 00240 /** 00241 * Used to generate error messages during path lookup. 00242 * \param end 00243 * The last component of 'parents' to include in the returned string; 00244 * this is typically the component that caused an error in path 00245 * traversal. 00246 * \return 00247 * The prefix of 'parents' up to and including the given end position. 00248 * This is returned as a slash-delimited string not including "/root". 00249 */ 00250 std::string 00251 parentsThrough(std::vector<std::string>::const_iterator end) const; 00252 00253 public: 00254 /** 00255 * Status and error message from the constructor. Possible errors are: 00256 * - INVALID_ARGUMENT if path is malformed. 00257 */ 00258 Result result; 00259 00260 /** 00261 * The exact argument given to the constructor. 00262 */ 00263 std::string symbolic; 00264 /** 00265 * The directories needed to traverse to get to the target. 00266 * This usually begins with "root" to get from the super root to the root 00267 * directory, then includes the components of the symbolic path up to but 00268 * not including the target. If the symbolic path is "/", this will be 00269 * empty. 00270 */ 00271 std::vector<std::string> parents; 00272 /** 00273 * The final component of the path. 00274 * This is usually at the end of the symbolic path. If the symbolic path is 00275 * "/", this will be "root", used to get from the super root to the root 00276 * directory. 00277 */ 00278 std::string target; 00279 }; 00280 00281 } // LogCabin::Tree::Internal 00282 00283 /** 00284 * This is an in-memory, hierarchical key-value store. 00285 * TODO(ongaro): Document how this fits into the rest of the system. 00286 */ 00287 class Tree { 00288 public: 00289 /** 00290 * Constructor. 00291 */ 00292 Tree(); 00293 00294 /** 00295 * Write the tree to the given stream. 00296 */ 00297 void dumpSnapshot(Core::ProtoBuf::OutputStream& stream) const; 00298 00299 /** 00300 * Load the tree from the given stream. 00301 * \warning 00302 * This will blow away any existing files and directories. 00303 */ 00304 void loadSnapshot(Core::ProtoBuf::InputStream& stream); 00305 00306 /** 00307 * Verify that the file at path has the given contents. 00308 * \param path 00309 * The path to the file that must have the contents specified in 00310 * 'contents'. 00311 * \param contents 00312 * The contents that the file specified by 'path' should have for an 00313 * OK response. An OK response is also returned if 'contents' is the 00314 * empty string and the file specified by 'path' does not exist. 00315 * \return 00316 * Status and error message. Possible errors are: 00317 * - CONDITION_NOT_MET upon any error. 00318 */ 00319 Result 00320 checkCondition(const std::string& path, 00321 const std::string& contents) const; 00322 00323 /** 00324 * Make sure a directory exists at the given path. 00325 * Create parent directories listed in path as necessary. 00326 * \param path 00327 * The path where there should be a directory after this call. 00328 * \return 00329 * Status and error message. Possible errors are: 00330 * - INVALID_ARGUMENT if path is malformed. 00331 * - TYPE_ERROR if a parent of path is a file. 00332 * - TYPE_ERROR if path exists but is a file. 00333 */ 00334 Result 00335 makeDirectory(const std::string& path); 00336 00337 /** 00338 * List the contents of a directory. 00339 * \param path 00340 * The directory whose direct children to list. 00341 * \param[out] children 00342 * This will be replaced by a listing of the names of the directories 00343 * and files that the directory at 'path' immediately contains. The 00344 * names of directories in this listing will have a trailing slash. 00345 * The order is first directories (sorted lexicographically), then 00346 * files (sorted lexicographically). 00347 * \return 00348 * Status and error message. Possible errors are: 00349 * - INVALID_ARGUMENT if path is malformed. 00350 * - LOOKUP_ERROR if a parent of path does not exist. 00351 * - LOOKUP_ERROR if path does not exist. 00352 * - TYPE_ERROR if a parent of path is a file. 00353 * - TYPE_ERROR if path exists but is a file. 00354 */ 00355 Result 00356 listDirectory(const std::string& path, 00357 std::vector<std::string>& children) const; 00358 00359 /** 00360 * Make sure a directory does not exist. 00361 * Also removes all direct and indirect children of the directory. 00362 * 00363 * If called with the root directory, this will remove all descendants but 00364 * not actually remove the root directory; it will still return status OK. 00365 * 00366 * \param path 00367 * The path where there should not be a directory after this call. 00368 * \return 00369 * Status and error message. Possible errors are: 00370 * - INVALID_ARGUMENT if path is malformed. 00371 * - TYPE_ERROR if a parent of path is a file. 00372 * - TYPE_ERROR if path exists but is a file. 00373 */ 00374 Result 00375 removeDirectory(const std::string& path); 00376 00377 /** 00378 * Set the value of a file. 00379 * \param path 00380 * The path where there should be a file with the given contents after 00381 * this call. 00382 * \param contents 00383 * The new value associated with the file. 00384 * \return 00385 * Status and error message. Possible errors are: 00386 * - INVALID_ARGUMENT if path is malformed. 00387 * - INVALID_ARGUMENT if contents are too large to fit in a file. 00388 * - LOOKUP_ERROR if a parent of path does not exist. 00389 * - TYPE_ERROR if a parent of path is a file. 00390 * - TYPE_ERROR if path exists but is a directory. 00391 */ 00392 Result 00393 write(const std::string& path, const std::string& contents); 00394 00395 /** 00396 * Get the value of a file. 00397 * \param path 00398 * The path of the file whose contents to read. 00399 * \param contents 00400 * The current value associated with the file. 00401 * \return 00402 * Status and error message. Possible errors are: 00403 * - INVALID_ARGUMENT if path is malformed. 00404 * - LOOKUP_ERROR if a parent of path does not exist. 00405 * - LOOKUP_ERROR if path does not exist. 00406 * - TYPE_ERROR if a parent of path is a file. 00407 * - TYPE_ERROR if path is a directory. 00408 */ 00409 Result 00410 read(const std::string& path, std::string& contents) const; 00411 00412 /** 00413 * Make sure a file does not exist. 00414 * \param path 00415 * The path where there should not be a file after this call. 00416 * \return 00417 * Status and error message. Possible errors are: 00418 * - INVALID_ARGUMENT if path is malformed. 00419 * - TYPE_ERROR if a parent of path is a file. 00420 * - TYPE_ERROR if path exists but is a directory. 00421 */ 00422 Result 00423 removeFile(const std::string& path); 00424 00425 /** 00426 * Add metrics about the tree to the given structure. 00427 */ 00428 void 00429 updateServerStats(Protocol::ServerStats_Tree& tstats) const; 00430 00431 private: 00432 /** 00433 * Resolve the final next-to-last component of the given path (the target's 00434 * parent). 00435 * \param[in] path 00436 * The path whose parent directory to find. 00437 * \param[out] parent 00438 * Upon successful return, points to the target's parent directory. 00439 * \return 00440 * Status and error message. Possible errors are: 00441 * - LOOKUP_ERROR if a parent of path does not exist. 00442 * - TYPE_ERROR if a parent of path is a file. 00443 */ 00444 Result 00445 normalLookup(const Internal::Path& path, 00446 Internal::Directory** parent); 00447 00448 /** 00449 * Resolve the final next-to-last component of the given path (the target's 00450 * parent) -- const version. 00451 * \copydetails normalLookup 00452 */ 00453 Result 00454 normalLookup(const Internal::Path& path, 00455 const Internal::Directory** parent) const; 00456 00457 /** 00458 * Like normalLookup but creates parent directories as necessary. 00459 * \param[in] path 00460 * The path whose parent directory to find. 00461 * \param[out] parent 00462 * Upon successful return, points to the target's parent directory. 00463 * \return 00464 * Status and error message. Possible errors are: 00465 * - TYPE_ERROR if a parent of path is a file. 00466 */ 00467 Result 00468 mkdirLookup(const Internal::Path& path, Internal::Directory** parent); 00469 00470 /** 00471 * This directory contains the root directory. The super root has a single 00472 * child directory named "root", and the rest of the tree lies below 00473 * "root". This is just an implementation detail; this class prepends 00474 * "/root" to every path provided by the caller. 00475 * 00476 * This removes a lot of special-case branches because every operation now 00477 * has a name of a target within a parent directory -- even those operating 00478 * on the root directory. 00479 */ 00480 Internal::Directory superRoot; 00481 00482 // Server stats collected in updateServerStats. 00483 // Note that when a condition fails, the operation is not invoked, 00484 // so operations whose conditions fail are not counted as 'Attempted'. 00485 mutable uint64_t numConditionsChecked; 00486 mutable uint64_t numConditionsFailed; 00487 uint64_t numMakeDirectoryAttempted; 00488 uint64_t numMakeDirectorySuccess; 00489 mutable uint64_t numListDirectoryAttempted; 00490 mutable uint64_t numListDirectorySuccess; 00491 uint64_t numRemoveDirectoryAttempted; 00492 uint64_t numRemoveDirectoryParentNotFound; 00493 uint64_t numRemoveDirectoryTargetNotFound; 00494 uint64_t numRemoveDirectoryDone; 00495 uint64_t numRemoveDirectorySuccess; 00496 uint64_t numWriteAttempted; 00497 uint64_t numWriteSuccess; 00498 mutable uint64_t numReadAttempted; 00499 mutable uint64_t numReadSuccess; 00500 uint64_t numRemoveFileAttempted; 00501 uint64_t numRemoveFileParentNotFound; 00502 uint64_t numRemoveFileTargetNotFound; 00503 uint64_t numRemoveFileDone; 00504 uint64_t numRemoveFileSuccess; 00505 }; 00506 00507 00508 } // namespace LogCabin::Tree 00509 } // namespace LogCabin 00510 00511 #endif // LOGCABIN_TREE_TREE_H