How to use pre_get_posts to alter posts_per_page of category pages, where ‘posts_per_page’ will be dynamic

If you have pretty permalinks enabled and you visit a category archive, the query var that gets populated is category_name. The cat query var will only be populated after the query is run. So while your code will work in a template, it won’t work in a pre_get_posts action for this reason.

To fix this, you need to get the category ID from the category slug:

function my_limit_posts_per_cat_page( $query ){
    if( $query->is_main_query() && is_category() ){

        $cat_slug = get_query_var( 'category_name' );
        $category = get_term_by( 'slug', $cat_slug, 'category' );
        $cat_id = $category->term_id;

        // the rest of your code using $cat_id goes here...
    }
}
add_action( 'pre_get_posts', 'my_limit_posts_per_cat_page' );

Note one other addition here is the use of $query->is_main_query() to limit this code to run only on the main query.

My general suggestion when developing is to log everything and verify that the data you are working with is what you expect. Timing is everything in WordPress.