|
34 | 34 | #include <zend_exceptions.h>
|
35 | 35 |
|
36 | 36 | #include "library.h"
|
| 37 | +#include "cluster_library.h" |
37 | 38 |
|
38 | 39 | #include "php.h"
|
39 | 40 | #include "php_ini.h"
|
|
44 | 45 | ps_module ps_mod_redis = {
|
45 | 46 | PS_MOD(redis)
|
46 | 47 | };
|
| 48 | +ps_module ps_mod_redis_cluster = { |
| 49 | + PS_MOD(rediscluster) |
| 50 | +}; |
47 | 51 |
|
48 | 52 | typedef struct redis_pool_member_ {
|
49 | 53 |
|
@@ -333,7 +337,6 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses
|
333 | 337 | return session;
|
334 | 338 | }
|
335 | 339 |
|
336 |
| - |
337 | 340 | /* {{{ PS_READ_FUNC
|
338 | 341 | */
|
339 | 342 | PS_READ_FUNC(redis)
|
@@ -457,5 +460,249 @@ PS_GC_FUNC(redis)
|
457 | 460 | }
|
458 | 461 | /* }}} */
|
459 | 462 |
|
| 463 | +/** |
| 464 | + * Redis Cluster session handler functions |
| 465 | + */ |
| 466 | + |
| 467 | +/* Helper to extract timeout values */ |
| 468 | +static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_len, |
| 469 | + double *val) |
| 470 | +{ |
| 471 | + zval **z_val; |
| 472 | + |
| 473 | + if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS) { |
| 474 | + if (Z_TYPE_PP(z_val) == IS_STRING) { |
| 475 | + *val = atof(Z_STRVAL_PP(z_val)); |
| 476 | + } else { |
| 477 | + *val = Z_DVAL_PP(z_val); |
| 478 | + } |
| 479 | + } |
| 480 | +} |
| 481 | + |
| 482 | +/* Prefix a session key */ |
| 483 | +static char *cluster_session_key(redisCluster *c, const char *key, int keylen, |
| 484 | + int *skeylen, short *slot) { |
| 485 | + char *skey; |
| 486 | + |
| 487 | + *skeylen = keylen + c->flags->prefix_len; |
| 488 | + skey = emalloc(*skeylen); |
| 489 | + memcpy(skey, c->flags->prefix, c->flags->prefix_len); |
| 490 | + memcpy(skey + c->flags->prefix_len, key, keylen); |
| 491 | + |
| 492 | + *slot = cluster_hash_key(skey, *skeylen); |
| 493 | + |
| 494 | + return skey; |
| 495 | +} |
| 496 | + |
| 497 | +PS_OPEN_FUNC(rediscluster) { |
| 498 | + redisCluster *c; |
| 499 | + zval *z_conf, **z_val; |
| 500 | + HashTable *ht_conf, *ht_seeds; |
| 501 | + double timeout = 0, read_timeout = 0; |
| 502 | + int retval, prefix_len, failover = REDIS_FAILOVER_NONE; |
| 503 | + char *prefix; |
| 504 | + |
| 505 | + /* Parse configuration for session handler */ |
| 506 | + MAKE_STD_ZVAL(z_conf); |
| 507 | + array_init(z_conf); |
| 508 | + sapi_module.treat_data(PARSE_STRING, estrdup(save_path), z_conf TSRMLS_CC); |
| 509 | + |
| 510 | + /* Sanity check that we're able to parse and have a seeds array */ |
| 511 | + if (Z_TYPE_P(z_conf) != IS_ARRAY || |
| 512 | + zend_hash_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed"), (void**)&z_val) == FAILURE || |
| 513 | + Z_TYPE_PP(z_val) != IS_ARRAY) |
| 514 | + { |
| 515 | + zval_dtor(z_conf); |
| 516 | + efree(z_conf); |
| 517 | + return FAILURE; |
| 518 | + } |
| 519 | + |
| 520 | + /* Grab a copy of our config hash table and keep seeds array */ |
| 521 | + ht_conf = Z_ARRVAL_P(z_conf); |
| 522 | + ht_seeds = Z_ARRVAL_PP(z_val); |
| 523 | + |
| 524 | + /* Grab timeouts if they were specified */ |
| 525 | + session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout); |
| 526 | + session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout); |
| 527 | + |
| 528 | + /* Look for a specific prefix */ |
| 529 | + if (zend_hash_find(ht_conf, "prefix", sizeof("prefix"), (void**)&z_val) == SUCCESS && |
| 530 | + Z_TYPE_PP(z_val) == IS_STRING && Z_STRLEN_PP(z_val) > 0) |
| 531 | + { |
| 532 | + prefix = Z_STRVAL_PP(z_val); |
| 533 | + prefix_len = Z_STRLEN_PP(z_val); |
| 534 | + } else { |
| 535 | + prefix = "PHPREDIS_CLUSTER_SESSION:"; |
| 536 | + prefix_len = sizeof("PHPREDIS_CLUSTER_SESSION:")-1; |
| 537 | + } |
| 538 | + |
| 539 | + /* Look for a specific failover setting */ |
| 540 | + if (zend_hash_find(ht_conf, "failover", sizeof("failover"), (void**)&z_val) == SUCCESS && |
| 541 | + Z_TYPE_PP(z_val) == IS_STRING) |
| 542 | + { |
| 543 | + if (!strcasecmp(Z_STRVAL_PP(z_val), "error")) { |
| 544 | + failover = REDIS_FAILOVER_ERROR; |
| 545 | + } else if (!strcasecmp(Z_STRVAL_PP(z_val), "distribute")) { |
| 546 | + failover = REDIS_FAILOVER_DISTRIBUTE; |
| 547 | + } |
| 548 | + } |
| 549 | + |
| 550 | + c = cluster_create(timeout, read_timeout, failover); |
| 551 | + if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { |
| 552 | + /* Set up our prefix */ |
| 553 | + c->flags->prefix = estrndup(prefix, prefix_len); |
| 554 | + c->flags->prefix_len = prefix_len; |
| 555 | + |
| 556 | + PS_SET_MOD_DATA(c); |
| 557 | + retval = SUCCESS; |
| 558 | + } else { |
| 559 | + cluster_free(c); |
| 560 | + retval = FAILURE; |
| 561 | + } |
| 562 | + |
| 563 | + /* Cleanup */ |
| 564 | + zval_dtor(z_conf); |
| 565 | + efree(z_conf); |
| 566 | + |
| 567 | + return retval; |
| 568 | +} |
| 569 | + |
| 570 | +/* {{{ PS_READ_FUNC |
| 571 | + */ |
| 572 | +PS_READ_FUNC(rediscluster) { |
| 573 | + redisCluster *c = PS_GET_MOD_DATA(); |
| 574 | + clusterReply *reply; |
| 575 | + char *cmd, *skey; |
| 576 | + int cmdlen, skeylen; |
| 577 | + short slot; |
| 578 | + |
| 579 | + /* Set up our command and slot information */ |
| 580 | + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); |
| 581 | + cmdlen = redis_cmd_format_static(&cmd, "GET", "s", skey, skeylen); |
| 582 | + efree(skey); |
| 583 | + |
| 584 | + /* Attempt to kick off our command */ |
| 585 | + c->readonly = 1; |
| 586 | + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { |
| 587 | + efree(cmd); |
| 588 | + return FAILURE; |
| 589 | + } |
| 590 | + |
| 591 | + /* Clean up command */ |
| 592 | + efree(cmd); |
| 593 | + |
| 594 | + /* Attempt to read reply */ |
| 595 | + reply = cluster_read_resp(c TSRMLS_CC); |
| 596 | + if (!reply || c->err) { |
| 597 | + if (reply) cluster_free_reply(reply, 1); |
| 598 | + return FAILURE; |
| 599 | + } |
| 600 | + |
| 601 | + /* Push reply value to caller */ |
| 602 | + *val = reply->str; |
| 603 | + *vallen = reply->len; |
| 604 | + |
| 605 | + /* Clean up */ |
| 606 | + cluster_free_reply(reply, 0); |
| 607 | + |
| 608 | + /* Success! */ |
| 609 | + return SUCCESS; |
| 610 | +} |
| 611 | + |
| 612 | +/* {{{ PS_WRITE_FUNC |
| 613 | + */ |
| 614 | +PS_WRITE_FUNC(rediscluster) { |
| 615 | + redisCluster *c = PS_GET_MOD_DATA(); |
| 616 | + clusterReply *reply; |
| 617 | + char *cmd, *skey; |
| 618 | + int cmdlen, skeylen; |
| 619 | + short slot; |
| 620 | + |
| 621 | + /* Set up command and slot info */ |
| 622 | + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); |
| 623 | + cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, |
| 624 | + INI_INT("session.gc_maxlifetime"), |
| 625 | + val, vallen); |
| 626 | + efree(skey); |
| 627 | + |
| 628 | + /* Attempt to send command */ |
| 629 | + c->readonly = 0; |
| 630 | + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { |
| 631 | + efree(cmd); |
| 632 | + return FAILURE; |
| 633 | + } |
| 634 | + |
| 635 | + /* Clean up our command */ |
| 636 | + efree(cmd); |
| 637 | + |
| 638 | + /* Attempt to read reply */ |
| 639 | + reply = cluster_read_resp(c TSRMLS_CC); |
| 640 | + if (!reply || c->err) { |
| 641 | + if (reply) cluster_free_reply(reply, 1); |
| 642 | + return FAILURE; |
| 643 | + } |
| 644 | + |
| 645 | + /* Clean up*/ |
| 646 | + cluster_free_reply(reply, 1); |
| 647 | + |
| 648 | + return SUCCESS; |
| 649 | +} |
| 650 | + |
| 651 | +/* {{{ PS_DESTROY_FUNC(rediscluster) |
| 652 | + */ |
| 653 | +PS_DESTROY_FUNC(rediscluster) { |
| 654 | + redisCluster *c = PS_GET_MOD_DATA(); |
| 655 | + clusterReply *reply; |
| 656 | + char *cmd, *skey; |
| 657 | + int cmdlen, skeylen; |
| 658 | + short slot; |
| 659 | + |
| 660 | + /* Set up command and slot info */ |
| 661 | + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); |
| 662 | + cmdlen = redis_cmd_format_static(&cmd, "DEL", "s", skey, skeylen); |
| 663 | + efree(skey); |
| 664 | + |
| 665 | + /* Attempt to send command */ |
| 666 | + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { |
| 667 | + if (reply) cluster_free_reply(reply, 1); |
| 668 | + efree(cmd); |
| 669 | + return FAILURE; |
| 670 | + } |
| 671 | + |
| 672 | + /* Clean up our command */ |
| 673 | + efree(cmd); |
| 674 | + |
| 675 | + /* Attempt to read reply */ |
| 676 | + reply = cluster_read_resp(c TSRMLS_CC); |
| 677 | + if (!reply || c->err) { |
| 678 | + if (reply) cluster_free_reply(reply, 1); |
| 679 | + return FAILURE; |
| 680 | + } |
| 681 | + |
| 682 | + /* Clean up our reply */ |
| 683 | + cluster_free_reply(reply, 1); |
| 684 | + |
| 685 | + return SUCCESS; |
| 686 | +} |
| 687 | + |
| 688 | +/* {{{ PS_CLOSE_FUNC |
| 689 | + */ |
| 690 | +PS_CLOSE_FUNC(rediscluster) |
| 691 | +{ |
| 692 | + redisCluster *c = PS_GET_MOD_DATA(); |
| 693 | + if (c) { |
| 694 | + cluster_free(c); |
| 695 | + PS_SET_MOD_DATA(NULL); |
| 696 | + } |
| 697 | + return SUCCESS; |
| 698 | +} |
| 699 | + |
| 700 | +/* {{{ PS_GC_FUNC |
| 701 | + */ |
| 702 | +PS_GC_FUNC(rediscluster) { |
| 703 | + return SUCCESS; |
| 704 | +} |
| 705 | + |
460 | 706 | #endif
|
| 707 | + |
461 | 708 | /* vim: set tabstop=4 expandtab: */
|
0 commit comments