-
Notifications
You must be signed in to change notification settings - Fork 7.9k
oci8 - Implementation of Oracle TAF Callback (PHP7) #2459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Adds support for the Transparent Application Failover Callback. The php_oci_connection struct got a char* added which will contain the callback function, it should be set to PHP_OCI_TAF_DISABLE_CALLBACK at the end of a php request for permanent connections so that, if a TAF callback occurs, no userspace function will be called. Maybe add support for registering object functions (via array), currently the register function only accepts a string. I didn't know how to implement it correctly. As a failover occurs very rarely it might be better to not keep the cache when saving the zend_fcall_info. Things to do [ ] config.m4 needs to compile oci8_failover.c [ ] Check if correctly implemented (especially for multithreading) [ ] Add support for registering callback function via array
Two examples to demonstrate how to use TAF callbacks: examples.zip The basic concept for testing |
ext/oci8/oci8_interface.c
Outdated
zval *z_connection; | ||
php_oci_connection *connection; | ||
char *callback = NULL; | ||
int callback_len = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be size_t
ext/oci8/oci8_failover.c
Outdated
} | ||
|
||
/* Create zval */ | ||
zval retval, callback, params[3]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now we're still on C89, so variable declarations should happen at the top of the function.
Moved variable declarations to the function beginning (C89)
* End: | ||
* vim600: noet sw=4 ts=4 fdm=marker | ||
* vim<600: noet sw=4 ts=4 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline at EOF?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the examples and updates for PHP7.
Regarding the 3 tasks to do:
- config.m4 needs to compile oci8_failover.c
Add oci8_failover.c in config.m4 at Line 338 and 409. - Check if correctly implemented (especially for multithreading)
I've tested the implementation on Apache where fpm processes concurrent requests which registered TAF callbacks individually and it works fine.
At the moment, the implementation looks good. I'm asking an Oracle HA expert to take another look at the code. Meanwhile, I will do stress testing.
- Add support for registering callback function via array
Shall we use zval to achieve closure support instead of using zend_fcall_info as Nikita suggested?
I've tried the way Nikita suggested on my side and it seems to work. Please see my changes inline. @nikic Can you please review my changes?
@KoenigsKind If you don't have time to apply the change, I'm also happy to take over the PR and add it from my side.
ext/oci8/php_oci8_int.h
Outdated
@@ -531,6 +535,13 @@ ZEND_BEGIN_MODULE_GLOBALS(oci) /* {{{ Module globals */ | |||
char *edition; | |||
ZEND_END_MODULE_GLOBALS(oci) /* }}} */ | |||
|
|||
/* {{{ transparent failover related prototypes */ | |||
|
|||
int php_oci_register_taf_callback(php_oci_connection *connection, char *callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Define callback as zval*
ext/oci8/php_oci8_int.h
Outdated
@@ -164,6 +166,8 @@ typedef struct { | |||
#ifdef HAVE_OCI8_DTRACE | |||
char *client_id; /* The oci_set_client_identifier() value */ | |||
#endif | |||
|
|||
char *taf_callback; /* The Oracle TAF callback function in the userspace */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Define taf_callback as zval.
ext/oci8/oci8_interface.c
Outdated
{ | ||
zval *z_connection; | ||
php_oci_connection *connection; | ||
char *callback = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 51-56 to be:
zval *callback;
zend_string *callback_name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!", &z_connection, &callback) == FAILURE) {
return;
}
if (callback) {
if(!zend_is_callable(callback, 0, &callback_name)) {
php_error_docref(NULL, E_WARNING, "function '%s' is not callable", ZSTR_VAL(callback_name));
zend_string_release(callback_name);
RETURN_FALSE;
}
zend_string_release(callback_name);
}
ext/oci8/oci8_failover.c
Outdated
sb4 returnValue = 0; | ||
|
||
/* Check if userspace callback function was disabled */ | ||
if (!fo_ctx->taf_callback || !strcmp(PHP_OCI_TAF_DISABLE_CALLBACK, fo_ctx->taf_callback)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 56-69 to be:
if(Z_ISUNDEF(fo_ctx->taf_callback)) {
return 0;
}
/* Initialize zval */
ZVAL_RES(¶ms[0], fo_ctx->id);
ZVAL_LONG(¶ms[1], fo_event);
ZVAL_LONG(¶ms[2], fo_type);
/* Call user function (if possible) */
if (call_user_function(EG(function_table), NULL, &fo_ctx->taf_callback, &retval, 3, params TSRMLS_CC) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call taf callback function, is it defined?");
}
ext/oci8/oci8_failover.c
Outdated
ZVAL_NULL(¶ms[0]); | ||
|
||
/* Cleanup */ | ||
zval_dtor(&callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove line 80.
ext/oci8/oci8_failover.c
Outdated
int php_oci_register_taf_callback(php_oci_connection *connection, char *callback) | ||
{ | ||
sword errstatus; | ||
char *oldCallback = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 104-129 to be:
int registered = 0;
/* temporary failover callback structure */
OCIFocbkStruct failover;
if (!callback) {
/* Disable callback */
if (Z_ISUNDEF(connection->taf_callback)) {
return 0; // Nothing to disable
}
registered = 1;
zval_ptr_dtor(&connection->taf_callback);
ZVAL_UNDEF(&connection->taf_callback);
} else {
if (!Z_ISUNDEF(connection->taf_callback)) {
registered = 1;
zval_ptr_dtor(&connection->taf_callback);
ZVAL_UNDEF(&connection->taf_callback);
}
/* Set userspace callback function */
ZVAL_COPY(&connection->taf_callback, callback);
}
/* OCI callback function already registered */
if (registered) {
return 0;
}
ext/oci8/oci8_failover.c
Outdated
PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (connection->server, (ub4) OCI_HTYPE_SERVER, (void *) &failover, (ub4) 0, (ub4) OCI_ATTR_FOCBK, connection->err)); | ||
|
||
if (errstatus != OCI_SUCCESS) { | ||
pefree(connection->taf_callback, connection->is_persistent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 141 to be:
zval_ptr_dtor(&connection->taf_callback);
ZVAL_UNDEF(&connection->taf_callback);
ext/oci8/oci8.c
Outdated
@@ -2225,6 +2246,12 @@ static int php_oci_connection_close(php_oci_connection *connection) | |||
connection->client_id = NULL; | |||
} | |||
#endif /* HAVE_OCI8_DTRACE */ | |||
|
|||
if (connection->taf_callback) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 2250-2253 to be:
if (!Z_ISUNDEF(connection->taf_callback)) {
zval_ptr_dtor(&connection->taf_callback);
ZVAL_UNDEF(&connection->taf_callback);
}
Also add the following after Line 1953, 1968 and 1978 to initialize connection->taf_callback:
ZVAL_UNDEF(&connection->taf_callback);
ext/oci8/oci8.c
Outdated
@@ -2667,6 +2694,11 @@ static int php_oci_persistent_helper(zval *zv) | |||
if (le->type == le_pconnection) { | |||
connection = (php_oci_connection *)le->ptr; | |||
|
|||
/* Remove TAF callback function as it's bound to current request */ | |||
if (connection->used_this_request && connection->taf_callback) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 2698 to be:
if (connection->used_this_request && !Z_ISUNDEF(connection->taf_callback)) {
@tianfyang Your suggested changes look fine to me. Only comment I have is that in PHP 7 the |
Implementation as suggested by @tianfyang oci_register_taf_callback now accepts any type of zval and config.m4 also compiles oci8_failover.c
Sorry, for the delay. Somehow I didn't get a notification... @tianfyang Thanks for the implementation I applied it and pushed it to the branch. I also added you as a collaborator, so feel free to push changes yourself. If you want I can also transfer this repo to you, so you get the ownership; I guess you would also get the ownership of this PR by doing this. About the change to zval, I realized it currently uses only one state (undef), this causes the following problem: Let's say we have a script that creates a permanent connection and registers a failover callback function. If we call this script twice:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KoenigsKind Thanks for applying the change and adding me as a collaborator.
It's really a good point to have a second state to avoid duplicate C callback registration in a persistent connection (which is functionally equivalent to your PHP_OCI_TAF_DISABLE_CALLBACK. Sorry I missed it). Please see my modification inline and feel free to comment or edit if you see anything missing or not making sense.
ext/oci8/oci8_failover.c
Outdated
|
||
/* Default return value */ | ||
sb4 returnValue = 0; | ||
|
||
/* Check if userspace callback function was disabled */ | ||
if (!fo_ctx->taf_callback || !strcmp(PHP_OCI_TAF_DISABLE_CALLBACK, fo_ctx->taf_callback)) { | ||
if (Z_ISUNDEF(fo_ctx->taf_callback)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 56 to:
if (Z_ISUNDEF(fo_ctx->taf_callback) || Z_ISNULL(fo_ctx->taf_callback)) {
ext/oci8/oci8_failover.c
Outdated
|
||
/* temporary failover callback structure */ | ||
OCIFocbkStruct failover; | ||
|
||
if (!callback) { | ||
/* Disable callback */ | ||
if (!connection->taf_callback || !strcmp(PHP_OCI_TAF_DISABLE_CALLBACK, connection->taf_callback)) { | ||
if (Z_ISUNDEF(connection->taf_callback)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 109 to:
if (Z_ISUNDEF(connection->taf_callback) || Z_ISNULL(connection->taf_callback)) {
ext/oci8/oci8_failover.c
Outdated
} | ||
registered = 1; | ||
zval_ptr_dtor(&connection->taf_callback); | ||
ZVAL_UNDEF(&connection->taf_callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 115 to:
ZVAL_NULL(&connection->taf_callback);
ext/oci8/oci8_failover.c
Outdated
zval_ptr_dtor(&connection->taf_callback); | ||
ZVAL_UNDEF(&connection->taf_callback); | ||
} else { | ||
if (!Z_ISUNDEF(connection->taf_callback)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 117 to:
if (!Z_ISUNDEF(connection->taf_callback) && !Z_ISNULL(connection->taf_callback)) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's set to null, it should still set registered to 1, so instead I would change the block at line 117 to:
if (!Z_ISUNDEF(connection->taf_callback)) {
registered = 1;
if (!Z_ISNULL(connection->taf_callback)) {
zval_ptr_dtor(&connection->taf_callback);
ZVAL_NULL(&connection->taf_callback);
}
}
ext/oci8/oci8_failover.c
Outdated
if (!Z_ISUNDEF(connection->taf_callback)) { | ||
registered = 1; | ||
zval_ptr_dtor(&connection->taf_callback); | ||
ZVAL_UNDEF(&connection->taf_callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 120 to:
ZVAL_NULL(&connection->taf_callback);
ext/oci8/oci8.c
Outdated
connection->taf_callback = NULL; | ||
if (!Z_ISUNDEF(connection->taf_callback)) { | ||
zval_ptr_dtor(&connection->taf_callback); | ||
ZVAL_UNDEF(&connection->taf_callback); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 2253 - 2256 to be:
if (!Z_ISUNDEF(connection->taf_callback)) {
if (!Z_ISNULL(connection->taf_callback)) /* If it's NULL, then its value should be freed already */
zval_ptr_dtor(&connection->taf_callback);
ZVAL_UNDEF(&connection->taf_callback);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more things that should be changed. (Together with the review of @tianfyang)
Also Line 96 of php_oci8_int.h can be removed. Couldn't create a comment as the line didn't change in this commit.
ext/oci8/oci8_failover.c
Outdated
@@ -101,30 +99,33 @@ int php_oci_disable_taf_callback(php_oci_connection *connection) | |||
int php_oci_register_taf_callback(php_oci_connection *connection, char *callback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 99 to:
int php_oci_register_taf_callback(php_oci_connection *connection, zval *callback)
ext/oci8/oci8.c
Outdated
@@ -2695,7 +2698,7 @@ static int php_oci_persistent_helper(zval *zv) | |||
connection = (php_oci_connection *)le->ptr; | |||
|
|||
/* Remove TAF callback function as it's bound to current request */ | |||
if (connection->used_this_request && connection->taf_callback) { | |||
if (connection->used_this_request && !Z_ISUNDEF(connection->taf_callback)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change Line 2701 to:
if (connection->used_this_request && !Z_ISUNDEF(connection->taf_callback) && !Z_ISNULL(connection->taf_callback)) {
@KoenigsKind I have tested your latest change and it works fine. Can you please apply the change and commit? I believe we are almost good to merge. |
ext/oci8/oci8_failover.c
Outdated
+----------------------------------------------------------------------+ | ||
*/ | ||
|
||
/* $Id$ */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
/* {{{ transparent failover related prototypes */ | ||
|
||
int php_oci_register_taf_callback(php_oci_connection *connection, zval *callback); | ||
int php_oci_disable_taf_callback(php_oci_connection *connection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we return bool instead of int here to align with other oci8 functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a look at the other functions, do you mean to still return an int, but to return 1 on success and 0 on failure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake. I was looking at the wrong prototype. For php_oci functions, it should return an int and return 0 on success and 1 on failure. Sorry I misled you.
Implemented a second state for php_oci_connection.taf_callback to prevent duplicate C callback registration in a persistent connection. Changed taf callback registration method to simply return 1 on success and 0 on failure.
ext/oci8/oci8_interface.c
Outdated
@@ -42,6 +42,29 @@ | |||
#define OCI_STMT_CALL 10 | |||
#endif | |||
|
|||
/* {{{ proto bool oci_register_taf_callback( resource connection [, string callback] ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now it should be:
oci_register_taf_callback( resource connection [, mixed callback]
oci8_failover functions now return 0 on success and 1 on failure
Is there anything left I can help with? I was not able to find anymore bugs. |
We prepared a user doc for this new feature in OCI8 and had a talk with an Oracle HA expert reviewing the implementation and the doc. Everything looks good now. @cjbj will probably do the merge sometime next week. |
Comment on behalf of sixd at php.net: Merged to 7.x - thanks! |
@KoenigsKind a new OCI8 2.1.7 bundle on PECL has this change. |
Adds support for the Transparent Application Failover Callback.
The
php_oci_connection
struct got achar*
added which will contain the callback function, it should be set toPHP_OCI_TAF_DISABLE_CALLBACK
at the end of a php request for permanent connections so that, if a TAF callback occurs, no userspace function will be called.Maybe add support for registering object functions (via array), currently the register function only accepts a string. I didn't know how to implement it correctly. As a failover occurs very rarely it might be better to not keep the cache when saving the
zend_fcall_info
.Original pull request
Original pull request for PHP 5: #2405
Things to do