1 module memcache.memcache; 2 import std.string; 3 import std.array; 4 import core.stdc.string; 5 import core.stdc.time; 6 import std.conv; 7 8 public import memcache; 9 10 pragma(inline,true) 11 string fromCString(const char* cstring) 12 { 13 return cast(string)(fromStringz(cstring)); 14 } 15 16 nothrow: 17 class Memcache 18 { 19 public: 20 21 this() 22 { 23 memc_ = memcached(null, 0); 24 } 25 26 this(string config) 27 { 28 memc_ = memcached(config.ptr, config.length); 29 } 30 31 this(string hostname, in_port_t port) 32 { 33 memc_ = memcached(null, 0); 34 if (memc_) 35 { 36 hostname ~= "\0"; 37 memcached_server_add(memc_, hostname.ptr, port); 38 } 39 } 40 41 this(memcached_st* clone) 42 { 43 memc_ = memcached_clone(null, clone); 44 } 45 46 ~this() 47 { 48 memcached_free(memc_); 49 } 50 51 /** 52 * Get the internal memcached_st * 53 */ 54 55 memcached_st * getImpl() 56 { 57 return memc_; 58 } 59 60 /** 61 * Return an error string for the given return structure. 62 * 63 * @param[in] rc a memcached_return_t structure 64 * @return error string corresponding to given return code in the library. 65 */ 66 const string getError(memcached_return_t rc) 67 { 68 /* first parameter to strerror is unused */ 69 70 return fromCString(memcached_strerror(null, rc)); 71 } 72 73 bool error(out string error_message) const 74 { 75 if (memcached_failed(memcached_last_error(memc_))) 76 { 77 error_message = fromCString(memcached_last_error_message(memc_)); 78 return true; 79 } 80 81 return false; 82 } 83 84 bool error() const 85 { 86 if (memcached_failed(memcached_last_error(memc_))) 87 { 88 return true; 89 } 90 91 return false; 92 } 93 94 bool error(out memcached_return_t arg) const 95 { 96 arg = memcached_last_error(memc_); 97 return memcached_failed(arg); 98 } 99 100 bool setBehavior(memcached_behavior_t flag, ulong data) 101 { 102 return (memcached_success(memcached_behavior_set(memc_, flag, data))); 103 } 104 105 ulong getBehavior(memcached_behavior_t flag) 106 { 107 return memcached_behavior_get(memc_, flag); 108 } 109 110 /** 111 * Configure the memcache object 112 * 113 * @param[in] in_config configuration 114 * @return true on success; false otherwise 115 */ 116 bool configure(string configuration) 117 { 118 memcached_st* new_memc = memcached(configuration.ptr, configuration.length); 119 120 if (new_memc) 121 { 122 memcached_free(memc_); 123 memc_ = new_memc; 124 125 return true; 126 } 127 128 return false; 129 } 130 131 /** 132 * Add a server to the list of memcached servers to use. 133 * 134 * @param[in] server_name name of the server to add 135 * @param[in] port port number of server to add 136 * @return true on success; false otherwise 137 */ 138 bool addServer(string server_name, in_port_t port) 139 { 140 server_name ~= "\0"; 141 return memcached_success(memcached_server_add(memc_, server_name.ptr, port)); 142 } 143 144 /** 145 * Remove a server from the list of memcached servers to use. 146 * 147 * @param[in] server_name name of the server to remove 148 * @param[in] port port number of server to remove 149 * @return true on success; false otherwise 150 */ 151 /* 152 bool removeServer(const string server_name, in_port_t port) 153 { 154 string str = "," ~ server_name ~":" ~ to!string(port); 155 156 memcached_return_t rc= memcached_server_remove(toStringz(str)); 157 158 return memcached_success(memcached_return_t); 159 } 160 */ 161 /** 162 * Fetches an individual value from the server. mget() must always 163 * be called before using this method. 164 * 165 * @param[in] key key of object to fetch 166 * @param[out] ret_val store returned object in this vector 167 * @return a memcached return structure 168 */ 169 memcached_return_t fetch(string key, out byte[] ret_val, out uint flags, out ulong cas_value) 170 { 171 memcached_return_t rc; 172 173 memcached_result_st* result; 174 result = memcached_fetch_result(memc_, null, &rc); 175 if (result) 176 { 177 // Key 178 const char* ckey = memcached_result_key_value(result); 179 size_t len = memcached_result_key_length(result); 180 if (len > 0) 181 { 182 key = ckey[0 .. len].dup; 183 } 184 else 185 { 186 key = ""; 187 } 188 // Actual value, null terminated 189 const char* rev = memcached_result_value(result); 190 len = memcached_result_length(result); 191 ret_val = cast(byte[]) rev[0 .. len].dup; 192 // Misc 193 flags = memcached_result_flags(result); 194 cas_value = memcached_result_cas(result); 195 } 196 memcached_result_free(result); 197 198 return rc; 199 } 200 201 memcached_return_t fetch(string key, out byte[] ret_val,) 202 { 203 uint flags = 0; 204 ulong cas_value = 0; 205 206 return fetch(key, ret_val, flags, cas_value); 207 } 208 209 /** 210 * Fetches an individual value from the server. 211 * 212 * @param[in] key key of object whose value to get 213 * @param[out] ret_val object that is retrieved is stored in 214 * this vector 215 * @return true on success; false otherwise 216 */ 217 T get(T = string)(string key, lazy T v= T.init) 218 { 219 import core.stdc.stdlib; 220 221 uint flags = 0; 222 memcached_return_t rc; 223 size_t value_length = 0; 224 225 char* value = memcached_get(memc_, cast(const char*) key.ptr, 226 key.length, &value_length, &flags, &rc); 227 char[] ret_val; 228 if (value != null) 229 { 230 ret_val = value[0 .. value_length].dup; 231 return to!T(ret_val); 232 } 233 else 234 { 235 return v; 236 } 237 238 } 239 240 /** 241 * Fetches an individual from a server which is specified by 242 * the master_key parameter that is used for determining which 243 * server an object was stored in if key partitioning was 244 * used for storage. 245 * 246 * @param[in] master_key key that specifies server object is stored on 247 * @param[in] key key of object whose value to get 248 * @param[out] ret_val object that is retrieved is stored in 249 * this vector 250 * @return true on success; false otherwise 251 */ 252 T getByKey(T = string)(string master_key, string key, lazy T v= T.init) 253 { 254 import core.stdc.stdlib; 255 256 uint flags = 0; 257 memcached_return_t rc; 258 size_t value_length = 0; 259 260 char* value = memcached_get_by_key(memc_, 261 cast(const char*) master_key.ptr, master_key.length, 262 cast(const char*) key.ptr, key.length, &value_length, &flags, &rc); 263 char[] ret_val; 264 if (value) 265 { 266 267 ret_val = value[0 .. value_length].dup; 268 free(value); 269 return to!T(ret_val); 270 } 271 else 272 { 273 return v; 274 } 275 276 } 277 278 /** 279 * Selects multiple keys at once. This method always 280 * works asynchronously. 281 * 282 * @param[in] keys vector of keys to select 283 * @return true if all keys are found 284 285 bool mget(const std::vector<std::string> & keys) 286 { 287 std::vector<const char *> real_keys; 288 std::vector<size_t> key_len; 289 /* 290 * Construct an array which will contain the length 291 * of each of the strings in the input vector. Also, to 292 * interface with the memcached C API, we need to convert 293 * the vector of std::string's to a vector of char *. 294 */ 295 /* 296 real_keys.reserve(keys.length); 297 key_len.reserve(keys.length); 298 299 std::vector<std::string>::const_iterator it= keys.begin(); 300 301 while (it != keys.end()) 302 { 303 real_keys.push_back(const_cast<char *>((*it).ptr)); 304 key_len.push_back((*it).length); 305 ++it; 306 } 307 308 /* 309 * If the std::vector of keys is empty then we cannot 310 * call memcached_mget as we will get undefined behavior. 311 */ 312 /* 313 if (not real_keys.empty()) 314 { 315 return memcached_success(memcached_mget(memc_, &real_keys[0], &key_len[0], real_keys.length)); 316 } 317 318 return false; 319 } 320 */ 321 /** 322 * Writes an object to the server. If the object already exists, it will 323 * overwrite the existing object. This method always returns true 324 * when using non-blocking mode unless a network error occurs. 325 * 326 * @param[in] key key of object to write to server 327 * @param[in] value value of object to write to server 328 * @param[in] expiration time to keep the object stored in the server for 329 * @param[in] flags flags to store with the object 330 * @return true on succcess; false otherwise 331 */ 332 bool set(T = string)(string key, T value, int expiration = 0, uint flags = 0) 333 { 334 auto mvalue = to!string(value); 335 memcached_return_t rc = memcached_set(memc_, 336 cast(const char*) cast(const char*) key.ptr, key.length, 337 cast(const char*) mvalue.ptr, mvalue.length, cast(time_t) expiration, flags); 338 return memcached_success(rc); 339 } 340 /* 341 bool set(ref string key, 342 const char* value, const size_t value_length, 343 int expiration, 344 uint flags) 345 { 346 memcached_return_t rc= memcached_set(memc_, 347 cast(const char *)key.ptr, key.length, 348 value, value_length, 349 expiration, flags); 350 return memcached_success(rc); 351 } 352 */ 353 /** 354 * Writes an object to a server specified by the master_key parameter. 355 * If the object already exists, it will overwrite the existing object. 356 * 357 * @param[in] master_key key that specifies server to write to 358 * @param[in] key key of object to write to server 359 * @param[in] value value of object to write to server 360 * @param[in] expiration time to keep the object stored in the server for 361 * @param[in] flags flags to store with the object 362 * @return true on succcess; false otherwise 363 */ 364 bool setByKey(T = string)(string master_key, string key, T value, 365 int expiration = 0, uint flags = 0) 366 { 367 string mvalue = to!string(value); 368 return memcached_success(memcached_set_by_key(memc_, 369 cast(const char*) master_key.ptr, master_key.length, 370 cast(const char*) key.ptr, key.length, cast(const char*) mvalue.ptr, 371 mvalue.length, cast(time_t) expiration, flags)); 372 } 373 374 /** 375 * Writes a list of objects to the server. Objects are specified by 376 * 2 vectors - 1 vector of keys and 1 vector of values. 377 * 378 * @param[in] keys vector of keys of objects to write to server 379 * @param[in] values vector of values of objects to write to server 380 * @param[in] expiration time to keep the objects stored in server for 381 * @param[in] flags flags to store with the objects 382 * @return true on success; false otherwise 383 */ 384 /* 385 bool setAll(string[] keys, 386 byte[][] values, 387 int expiration, 388 uint flags) 389 { 390 bool retval= true; 391 for (int i = 0; i < keys.length; ++i) { 392 retval= set(keys[i], values[i], expiration, flags); 393 } 394 std::vector<std::string>::const_iterator key_it= keys.begin(); 395 std::vector< std::vector<char> *>::const_iterator val_it= values.begin(); 396 while (key_it != keys.end()) 397 { 398 retval= set((*key_it), *(*val_it), expiration, flags); 399 if (retval == false) 400 { 401 return retval; 402 } 403 ++key_it; 404 ++val_it; 405 } 406 return retval; 407 } 408 */ 409 /** 410 * Writes a list of objects to the server. Objects are specified by 411 * a map of keys to values. 412 * 413 * @param[in] key_value_map map of keys and values to store in server 414 * @param[in] expiration time to keep the objects stored in server for 415 * @param[in] flags flags to store with the objects 416 * @return true on success; false otherwise 417 */ 418 /* 419 bool setAll(const std::map<const std::string, std::vector<char> >& key_value_map, 420 int expiration, 421 uint flags) 422 { 423 bool retval= true; 424 std::map<const std::string, std::vector<char> >::const_iterator it= key_value_map.begin(); 425 426 while (it != key_value_map.end()) 427 { 428 retval= set(it->first, it->second, expiration, flags); 429 if (retval == false) 430 { 431 // We should tell the user what the key that failed was 432 return false; 433 } 434 ++it; 435 } 436 437 return true; 438 } 439 */ 440 /** 441 * Increment the value of the object associated with the specified 442 * key by the offset given. The resulting value is saved in the value 443 * parameter. 444 * 445 * @param[in] key key of object in server whose value to increment 446 * @param[in] offset amount to increment object's value by 447 * @param[out] value store the result of the increment here 448 * @return true on success; false otherwise 449 */ 450 bool increment(string key, uint offset, ulong* value) 451 { 452 return memcached_success(memcached_increment(memc_, 453 cast(const char*) key.ptr, key.length, offset, value)); 454 } 455 456 /** 457 * Decrement the value of the object associated with the specified 458 * key by the offset given. The resulting value is saved in the value 459 * parameter. 460 * 461 * @param[in] key key of object in server whose value to decrement 462 * @param[in] offset amount to increment object's value by 463 * @param[out] value store the result of the decrement here 464 * @return true on success; false otherwise 465 */ 466 bool decrement(string key, uint offset, ulong* value) 467 { 468 return memcached_success(memcached_decrement(memc_, 469 cast(const char*) key.ptr, key.length, offset, value)); 470 } 471 472 /** 473 * Add an object with the specified key and value to the server. This 474 * function returns false if the object already exists on the server. 475 * 476 * @param[in] key key of object to add 477 * @param[in] value of object to add 478 * @return true on success; false otherwise 479 */ 480 bool add(T = string)(string key, T tvalue) 481 { 482 auto value = to!string(tvalue); 483 return memcached_success(memcached_add(memc_, cast(const char*) key.ptr, 484 key.length, cast(const char*) value.ptr, value.length, cast(time_t) 0, 485 cast(uint) 0)); 486 } 487 488 /** 489 * Add an object with the specified key and value to the server. This 490 * function returns false if the object already exists on the server. The 491 * server to add the object to is specified by the master_key parameter. 492 * 493 * @param[in[ master_key key of server to add object to 494 * @param[in] key key of object to add 495 * @param[in] value of object to add 496 * @return true on success; false otherwise 497 */ 498 bool addByKey(T = string)(string master_key, string key, T tvalue) 499 { 500 auto value = to!string(tvalue); 501 return memcached_success(memcached_add_by_key(memc_, 502 cast(const char*) master_key.ptr, master_key.length, 503 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 504 value.length, cast(time_t) 0, cast(uint) 0)); 505 } 506 507 /** 508 * Replaces an object on the server. This method only succeeds 509 * if the object is already present on the server. 510 * 511 * @param[in] key key of object to replace 512 * @param[in[ value value to replace object with 513 * @return true on success; false otherwise 514 */ 515 bool replace(T = string)(string key, T tvalue) 516 { 517 auto value = to!string(tvalue); 518 return memcached_success(memcached_replace(memc_, 519 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 520 value.length, cast(time_t) 0, cast(uint) 0)); 521 } 522 523 /** 524 * Replaces an object on the server. This method only succeeds 525 * if the object is already present on the server. The server 526 * to replace the object on is specified by the master_key param. 527 * 528 * @param[in] master_key key of server to replace object on 529 * @param[in] key key of object to replace 530 * @param[in[ value value to replace object with 531 * @return true on success; false otherwise 532 */ 533 bool replaceByKey(T = string)(string master_key, string key, T tvalue) 534 { 535 auto value = to!string(tvalue); 536 return memcached_success(memcached_replace_by_key(memc_, 537 cast(const char*) master_key.ptr, master_key.length, 538 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 539 value.length, cast(time_t) 0, cast(uint) 0)); 540 } 541 542 /** 543 * Places a segment of data before the last piece of data stored. 544 * 545 * @param[in] key key of object whose value we will prepend data to 546 * @param[in] value data to prepend to object's value 547 * @return true on success; false otherwise 548 */ 549 bool prepend(T = string)(string key, T tvalue) 550 { 551 auto value = to!string(tvalue); 552 return memcached_success(memcached_prepend(memc_, 553 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 554 value.length, cast(time_t) 0, cast(uint) 0)); 555 } 556 557 /** 558 * Places a segment of data before the last piece of data stored. The 559 * server on which the object where we will be prepending data is stored 560 * on is specified by the master_key parameter. 561 * 562 * @param[in] master_key key of server where object is stored 563 * @param[in] key key of object whose value we will prepend data to 564 * @param[in] value data to prepend to object's value 565 * @return true on success; false otherwise 566 */ 567 bool prependByKey(T = string)(string master_key, string key, T tvalue) 568 { 569 auto value = to!string(tvalue); 570 return memcached_success(memcached_prepend_by_key(memc_, 571 cast(const char*) master_key.ptr, master_key.length, 572 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 573 value.length, cast(time_t) 0, cast(uint) 0)); 574 } 575 576 /** 577 * Places a segment of data at the end of the last piece of data stored. 578 * 579 * @param[in] key key of object whose value we will append data to 580 * @param[in] value data to append to object's value 581 * @return true on success; false otherwise 582 */ 583 bool append(T = string)(string key, T tvalue) 584 { 585 auto value = to!string(tvalue); 586 return memcached_success(memcached_append(memc_, 587 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 588 value.length, cast(time_t) 0, cast(uint) 0)); 589 } 590 591 /** 592 * Places a segment of data at the end of the last piece of data stored. The 593 * server on which the object where we will be appending data is stored 594 * on is specified by the master_key parameter. 595 * 596 * @param[in] master_key key of server where object is stored 597 * @param[in] key key of object whose value we will append data to 598 * @param[in] value data to append to object's value 599 * @return true on success; false otherwise 600 */ 601 bool appendByKey(T = string)(string master_key, string key, T tvalue) 602 { 603 auto value = to!string(tvalue); 604 return memcached_success(memcached_append_by_key(memc_, 605 cast(const char*) master_key.ptr, master_key.length, 606 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 607 value.length, cast(time_t) 0, cast(uint) 0)); 608 } 609 610 /** 611 * Overwrite data in the server as long as the cas_arg value 612 * is still the same in the server. 613 * 614 * @param[in] key key of object in server 615 * @param[in] value value to store for object in server 616 * @param[in] cas_arg "cas" value 617 */ 618 bool cas(T = string)(string key, T tvalue, ulong cas_arg) 619 { 620 auto value = to!string(tvalue); 621 return memcached_success(memcached_cas(memc_, cast(const char*) key.ptr, 622 key.length, cast(const char*) value.ptr, value.length, 623 cast(time_t) 0, cast(uint) 0, cas_arg)); 624 } 625 626 /** 627 * Overwrite data in the server as long as the cas_arg value 628 * is still the same in the server. The server to use is 629 * specified by the master_key parameter. 630 * 631 * @param[in] master_key specifies server to operate on 632 * @param[in] key key of object in server 633 * @param[in] value value to store for object in server 634 * @param[in] cas_arg "cas" value 635 */ 636 bool casByKey(T = string)(string master_key, string key, T tvalue, ulong cas_arg) 637 { 638 auto value = to!string(tvalue); 639 return memcached_success(memcached_cas_by_key(memc_, 640 cast(const char*) master_key.ptr, master_key.length, 641 cast(const char*) key.ptr, key.length, cast(const char*) value.ptr, 642 value.length, cast(time_t) 0, cast(uint) 0, cas_arg)); 643 } 644 645 /** 646 * Delete an object from the server specified by the key given. 647 * 648 * @param[in] key key of object to delete 649 * @param[in] expiration time to delete the object after 650 * @return true on success; false otherwise 651 */ 652 bool del(string key, int expiration = 0) 653 { 654 return memcached_success(memcached_delete(memc_, 655 cast(const char*) key.ptr, key.length, cast(time_t) expiration)); 656 } 657 658 /** 659 * Delete an object from the server specified by the key given. 660 * 661 * @param[in] master_key specifies server to remove object from 662 * @param[in] key key of object to delete 663 * @param[in] expiration time to delete the object after 664 * @return true on success; false otherwise 665 */ 666 bool delByKey(string master_key, string key, int expiration = 0) 667 { 668 return memcached_success(memcached_delete_by_key(memc_, 669 cast(const char*) master_key.ptr, master_key.length, 670 cast(const char*) key.ptr, key.length, cast(time_t) expiration)); 671 } 672 673 /** 674 * Wipe the contents of memcached servers. 675 * 676 * @param[in] expiration time to wait until wiping contents of 677 * memcached servers 678 * @return true on success; false otherwise 679 */ 680 bool flush(int expiration = 0) 681 { 682 return memcached_success(memcached_flush(memc_, cast(time_t) expiration)); 683 } 684 685 /** 686 * Get the library version string. 687 * @return std::string containing a copy of the library version string. 688 */ 689 const string libVersion() 690 { 691 const char* ver = memcached_lib_version(); 692 return fromCString(ver); 693 } 694 695 /** 696 * Retrieve memcached statistics. Populate a std::map with the retrieved 697 * stats. Each server will map to another std::map of the key:value stats. 698 * 699 * @param[out] stats_map a std::map to be populated with the memcached 700 * stats 701 * @return true on success; false otherwise 702 */ 703 /* 704 bool getStats(std::map< std::string, std::map<std::string, std::string> >& stats_map) 705 { 706 memcached_return_t rc; 707 memcached_stat_st *stats= memcached_stat(memc_, null, &rc); 708 709 if (rc != MEMCACHED_SUCCESS && 710 rc != MEMCACHED_SOME_ERRORS) 711 { 712 return false; 713 } 714 715 uint server_count= memcached_server_count(memc_); 716 717 /* 718 * For each memcached server, construct a std::map for its stats and add 719 * it to the std::map of overall stats. 720 */ 721 /* 722 for (uint x= 0; x < server_count; x++) 723 { 724 const memcached_instance_st * instance= memcached_server_instance_by_position(memc_, x); 725 std::ostringstream strstm; 726 std::string server_name(memcached_server_name(instance)); 727 server_name.append(":"); 728 strstm << memcached_server_port(instance); 729 server_name.append(strstm.str()); 730 731 std::map<std::string, std::string> server_stats; 732 char **list= memcached_stat_get_keys(memc_, &stats[x], &rc); 733 for (char** ptr= list; *ptr; ptr++) 734 { 735 char *value= memcached_stat_get_value(memc_, &stats[x], *ptr, &rc); 736 server_stats[*ptr]= value; 737 free(value); 738 } 739 740 stats_map[server_name]= server_stats; 741 free(list); 742 } 743 744 memcached_stat_free(memc_, stats); 745 return true; 746 } 747 */ 748 private: 749 memcached_st* memc_; 750 };