save_post action not firing when save

You have some errors in your code. For example, you unset a not defined variable. Some lines bellow you try to use again a not defined variable….maybe this is what is causing the problems.

Can you try this? (Edited, I think is better get_posts than new WP_Query in this case)

function wpse_update_postmeta($post_id) {

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    $cat_id = get_the_category($post_id);
    $related_posts = array();

    if( !empty($cat_id) ) {

        $args = array(
        'posts_per_page' => 3,
        'orderby' => 'rand',
        'category' => $cat_id[0]->cat_ID,
    );

    $related_posts = get_posts($args);
    }

    if( count($related_posts) > 0 ) {

        $rand_id = array();

        foreach( $related_posts as $related_post ) {
            $rand_id[] = $related_post->ID;
        }

       update_post_meta($post_id, 'related_id', implode(",", $rand_id));
       //Uncomment the next line to check if you get here
       //error_log("Hello! I'm here");

    }

}
add_action('save_post', 'wpse_update_postmeta');