-
Notifications
You must be signed in to change notification settings - Fork 8k
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)
ext/oci8/oci8_failover.c
Outdated
| * End: | ||
| * vim600: noet sw=4 ts=4 fdm=marker | ||
| * vim<600: noet sw=4 ts=4 | ||
| */ No newline at end of file |
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?
tianfyang
left a comment
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
|
|
||
| /* {{{ 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
| 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
| } | ||
| #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
| 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
|
|
||
| /* 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
| 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);
| 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);
| 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);
}
KoenigsKind
left a comment
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
|
|
||
| /* {{{ php_oci_register_taf_callback() | ||
| Register a callback function for Oracle TAF */ | ||
| 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
|
|
||
| /* 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
| #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_connectionstruct got achar*added which will contain the callback function, it should be set toPHP_OCI_TAF_DISABLE_CALLBACKat 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