Drupal hook_cron execution order - drupal

What order does Drupal execute it's _cron hooks? It is important for a certain custom module I am developing and can't seem to find any documentation on it on the web. Maybe I'm searching for the wrong thing!

Drupal executes all of its hooks in the order based off of module weight. Module weight defaults to 0, and the secondary ordering is alphabetical by module name:
http://api.drupal.org/api/function/module_list/6

You can inspect and adjust the cron execution orders with the Supercron module. Some more details about this module (from its project page):
SuperCron is a complete replacement for Drupal's built-in Cron functionality. It allows you to:
See the list of all Cron hooks found in the enabled modules
Change the order in which cron hooks are called
Disable certain hooks
Run the tasks you choose in parallel, so that cron tasks will be executed all at once rather than one after the other
Identify the exceptions raised by individual hooks
Call hooks individually on demand (great for identifying problems)
Keep executing cron hooks that follow an exception, limiting the damage to only one module
Measure the time it takes for a cron hook to execute (we display the last call timings and the average timings)
Capture any output generated by the hooks
Change the way Cron behaves when the site is under load (this optional feature requires Throttle to be enabled)
Limit the IP addresses that may be allowed to call your cron scripts

For Drupal 8 you have to rearrange modules' implementation order in hook_module_implements_alter:
function YOUR_MODULE_module_implements_alter(&$implementations, $hook) {
// Move our hook_cron() implementation to the end of the list.
if ($hook == 'cron') {
$group = $implementations['YOUR_MODULE'];
unset($implementations['YOUR_MODULE']);
$implementations['YOUR_MODULE'] = $group;
}
}
If you'd like to call your hook_cron first:
function YOUR_MODULE_module_implements_alter(&$implementations, $hook) {
// Move our hook_cron() implementation to the top of the list.
if ($hook == 'cron') {
$group = $implementations['YOUR_MODULE'];
$implementations = [
'YOUR_MODULE' => $group,
] + $implementations;
}
}

Hooks execution is determinate from the weight of the module implementing them; the weightier module will be executed for last.

Related

Airflow - How to override default failure notification method

Would someone let me know if there is a way to override default failure notification method.
I am planning to send failure notification to SNS, however this means I will have to change all the existing DAG and add on_failure_callback method to it.
I was thinking if there is a way I can override existing notification method such that I don't need to change all the DAG.
or configure global hook for all the dags, such that I don't need to add on_failure_callback to all the dags.
You can use Cluster policy to mutate the task right after the DAG is parsed.
For example, this function could apply a specific queue property when using a specific operator, or enforce a task timeout policy, making sure that no tasks run for more than 48 hours. Here’s an example of what this may look like inside your airflow_local_settings.py:
def policy(task):
if task.__class__.__name__ == 'HivePartitionSensor':
task.queue = "sensor_queue"
if task.timeout > timedelta(hours=48):
task.timeout = timedelta(hours=48)
For Airflow 2.0, this policy should looks:
def task_policy(task):
if task.__class__.__name__ == 'HivePartitionSensor':
task.queue = "sensor_queue"
if task.timeout > timedelta(hours=48):
task.timeout = timedelta(hours=48)
The policy function has been renamed to task_policy.
In a similar way, you can modify other attributes, e.g. on_execute_callback, on_failure_callback, on_success_callback, on_retry_callback.
The airflow_local_settings.py file must be in one of the directories that are in sys.path. The easiest way to take advantage of this is that Airflow adds the directory ~/airflow/config to sys.path at startup, so you you need to create an ~/airfow/config/airflow_local_settings.py file.

How to avoid loops when writing cloud functions?

When writing event based cloud functions for firebase firestore it's common to update fields in the affected document, for example:
When a document of users collection is updated a function will trigger, let's say we want to determine the user info state and we have a completeInfo: boolean property, the function will have to perform another update so that the trigger will fire again, if we don't use a flag like needsUpdate: boolean to determine if excecuting the function we will have an infinite loop.
Is there any other way to approach this behavior? Or the situation is a consequence of how the database is designed? How could we avoid ending up in such scenario?
I have a few common approaches to Cloud Functions that transform the data:
Write the transformed data to a different document than the one that triggers the Cloud Function. This is by far the easier approach, since there is no additional code needed - and thus I can't make any mistakes in it. It also means there is no additional trigger, so you're not paying for that extra invocation.
Use granular triggers to ensure my Cloud Function only gets called when it needs to actually do some work. For example, many of my functions only need to run when the document gets created, so by using an onCreate trigger I ensure my code only gets run once, even if it then ends up updating the newly created document.
Write the transformed data into the existing document. In that case I make sure to have the checks for whether the transformation is needed in place before I write the actual code for the transformation. I prefer to not add flag fields, but use the existing data for this check.
A recent example is where I update an amount in a document, which then needs to be fanned out to all users:
exports.fanoutAmount = functions.firestore.document('users/{uid}').onWrite((change, context) => {
let old_amount = change.before && change.before.data() && change.before.data().amount ? change.before.data().amount : 0;
let new_amount = change.after.data().amount;
if (old_amount !== new_amount) {
// TODO: fan out to all documents in the collection
}
});
You need to take care to avoid writing a function that triggers itself infinitely. This is not something that Cloud Functions can do for you. Typically you do this by checking within your function if the work was previously done for the document that was modified in a previous invocation. There are several ways to do this, and you will have to implement something that meets your specific use case.
I would take this approach from an execution time perspective, this means that the function for each document will be run twice. Each time when the document is triggered, a field lastUpdate would be there with a timestamp and the function only updates the document if the time is older than my time - eg 10 seconds.

wordpress: how do actions get executed?

Do custom hooks that hook onto wordpress core get executed automatically by wordpress or do we have to call the do_action function on the custom made hook ourselves?
Example:
add_action("action_one","some_process",10);
add_action("action_one","some_different_process",11);
function some_process(){ //... }
function some_different_process(){ //... }
do_action("action_one");
Does some_process get executed first and then some_different_process gets executed after when do_action("action_one") is called?
If you create custom action hooks that exist within your custom theme or plugin, then yes, you have to call them via do_action in the appropriate location where you would want them to be triggered.
WordPress does not automatically find action hooks and trigger them. It has a number of built-in hooks which you can latch onto in order to run custom code, but if you create your own hooks, then you need to also set up the appropriate do_action call in order to run them.
To answer your other question regarding the execution order of some_process and some_different_process, some_process will be run before some_different_process because it had a lower priority number.
If you want an action to run early, give it a low number like 1 or 5, if you don't care where it runs or want it to run last, I usually use a much higher number like 100 or 1000. If two hooks are defined with the same priority, usually the one that got registered first will run before the other one with the same priority.
All functions that are hooked onto an action are automatically executed IF that action is called, but they are not called if the action is not triggered.
For example if you have:
add_action("action_one","some_function",10);
Then some_function will be called if action_one is triggered. If action_one is never triggered, some_function is not called.
do_action is a mechanism to manually trigger the action, though keep in mind it will trigger ANY hooks into that action, not just yours (unless you setup some filters).
Another example: let's say you setup a custom function to run on the action save_post:
add_action( 'save_post', 'top_secret_function' );
Then every time you a save a post your top_secret_function will run.
If you want to trigger save_post manually (without actually saving a post) you can do so with:
do_action( 'save_post' );
and it will trigger your top_secret_function, but this would generally not be recommended because it will also trigger any other function that is hooked into save_post (which many plugins and core files do).
You can setup custom actions using a combination of add_action and do_action.

add_action,add_filter,user_can ect in wordpress

I have developed wordpress for days,but I have found no way to these like:add_action,add_filter,user_can, I don't know what are the functions they refere to.
worse still,I don't know the parameter,today I want add a column to user list table admin panel,I fortunatelly found a tutorial, here is the code
add_filter( 'manage_users_columns', 'add_user_column');
function add_user_column( $columns){
$columns['available_stickers'] = __('Stickers Available', 'available_stickers');
return $columns;
}
add_filter('manage_users_custom_column', 'add_user_column_value', 10, 3);
function add_user_column_value( $value, $column_name, $user_id ){
if ( 'available_sticker' == $column_name)
$value = get_user_meta($user_id,"available_stickers",true);
return $value;
}
Even thought I made it, but I don't know where the parameter manage_users_columns comes or why I should use manage_users_columns but not other code? Puzzled
and also they have matched code like apply_filter etc.
some one can help me out of the maze,oops!
WordPress is beautifully designed because most of the actions it does are not executed directly, but through what are called actions and filters. This gives you, the developer, a possibility to hook onto any of these operations. Hooking means you can squeeze your own logic right in the middle of WP's logic, in a very clean way, only by declaring that you want things to be done where the corresponding hooks are used. More precisely:
Actions
So, for example, when a post is saved, WordPress does not just save the post, it does it by executing this line:
do_action( 'save_post', $post_ID, $post );
This is a way to give a name to this action (save_post). That means two things :
1) you can execute the exact same action by using the same line of code somewhere else.
2) you can add your own logic to be executed during this action. How? just by "adding" your custom functions to the action, like this :
add_action('save_post', 'name_of_the_function_you_want_to_execute_when_post_is_saved');
So 'save_post' is the name of the hook, add_action gives you the possibility to add your own function to the action for this hook and do_action actually executes the action.
Filters
Filters are similar to actions, but instead of being used when executing a command, they are used to treat a value, an object, a string or some text (when "filtering" it). Again, instead of just manipulating objects and strings in such a way that you would have no other possibility than dive into the core code to access it, WordPress cleverly does many of its "filtering" operations with a special action called apply_filters(). This gives you the possibility, like for actions, to actually add your own filters to the filters already applied. So when showing the content of a post, WP would execute :
apply_filters('the_content', $post->post_content);
The name of the hook is the_content. If you use :
add_filter('the_content', 'your_function_to_execute_to_further_filter_content');
you can have a function named your_function_to_execute_to_further_filter_content()that can take the content as a parameter and return the filtered version of it after whatever treatment you wish to apply to it. Then this treatment will get applied anywhere in WP where the hook the_content is used to execute these filters (including WP default ones, plug-ins filters that were added to the same hook, and your own).
So here, you are using these hooks so you can interfere with WP operations without messing WP core code. You declare the extra operations you wish to execute and you let WP aware of this. WP will then have the courtesy to execute your functions everytime the corresponding hook action or filter is being executed.

Drupal: How can one hook exit in a custom module be called in every page request?

How can one hook exit in a custom module be called in every page request of a drupal website?
Here is the hook exit code in a custom module:
<?php
function cec_monitoring_exit() {
if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') {
// database inserts here
}
}
?>
Is it because of the if condition or someting else? Because some of the custom modules are calling the hook 'cec_monitoring_exit()' but some other custom modules don't.
Thanks in advance.
Cheers,
Mark
I'm not sure what you mean by it is not called in some of the custom modules either, so just speculating here that you might mean custom pages provided by other modules:
As Jeremy and Googletorp stated already, your if block
if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') {
// database inserts here
}
will only evaluate to true if the user is requesting a full node page (e.g. 'node/42'). It will not match any other page like a term view (e.g. 'taxonomy/term/36') or the default frontpage ('node') or a view page provided by the views module (might/have/any/path), etc..
So your database inserts would only take place for node pages, and nothing else.
If you want to log every page request, you can just remove the if block and do your database insertions directly.
Edit after clarification in comments (cec_monitoring_exit() does not get invoked for some pages created by other modules):
I can only see two possible reasons for this.
The first reason would be an error occurring right after the page output, but before the invocation of your hook_exit() implementation (check your server logs for php errors on requests for the failing pages). If you take a look at 'index.php' (top level folder of your Drupal instance):
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$return = menu_execute_active_handler();
// Menu status constants are integers; page content is a string.
if (is_int($return)) {
switch ($return) {
case MENU_NOT_FOUND:
drupal_not_found();
break;
case MENU_ACCESS_DENIED:
drupal_access_denied();
break;
case MENU_SITE_OFFLINE:
drupal_site_offline();
break;
}
}
elseif (isset($return)) {
// Print any value (including an empty string) except NULL or undefined:
print theme('page', $return);
}
drupal_page_footer();
you can see that the page output is generated by printing the result of theme('page', $return). The invocation of hook_exit() happens right after that in drupal_page_footer().
So you should check (preferably via debugger, but you could also use print statements) if drupal_page_footer() gets executed on the pages in question at all. If it gets called, the error might occur in a hook_exit() implementation of another module that gets called before yours, so you'd need to check those.
The second reason would be if one of the modules would circumvent the standard Drupal execution flow by calling theme('page', ...) itself and stopped the execution afterwards. In this case, drupal_page_footer() would not be called, because the execution would have stopped long before during the call to menu_execute_active_handler(). Note that no established Drupal Module would do this, so it is pretty unlikely.
Besides these options, I have no further idea on what could cause this.
Hooks are called whenever their condition is met. This is done with the use of module_invoke_all(). This means that hooks that are called on every page request, will be invoked in every page request. In your example above, your hook wont do anything in some cases, but it will still be called since Drupal wont know when it actually will do something.
This waste of resource will be limited when pages are cached, since you only will need to run the hooks when page cached page is created. Another example is whenever a node is loaded with node_load, this will result in a lot of hooks being fired, and is thus quite expensive. Thus you usually want to avoid using this whenever possible, when you want to access something on a lot of nodes like their title.
#marknt15 what i think your failing to realize here is its all based on name so that hook being the hook_exit() in your case AKA _exit() is called when it is placed with a prefix of the calling modules name eg cec_monitoring_exit() will only work in the cec_monitoring module but in you other custom module named say marknt15_loves_drupal then it would meed to use hook_exit() like marknt15_loves_drupal_exit()
drupal then calls it and all other hooks with ...
call_user_func_array('modulename'_'hookname') or something very similar

Resources