How to prevent multiple post with same meta value being created simultaneously in WordPress (with ajax)

first of all at the end of using a new WP_Query it’s good pratice to always use wp_reset_postdata(); to be sure nothing had compromise your database.

$already_reserved = new WP_Query(array(
    'post_type' => 'appointments',
    'fields' => 'ids',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key' => 'datetime',
            'value' => $date_time, // timestamps
            'type' => 'numeric',
            'compare' => '='
        )
    ),
    'tax_query' => array(
        array(
            'taxonomy' => 'services',
            'field' => 'id',
            'terms' => $service_id,
        )
    )
  wp_reset_postdata();
));

and to answer your question, why don’t use another if when the button is pressed, right before the appointment is set on the database. like so

if ( !empty($already_reserved->posts) ) {
    $response="Sorry, this date is full.";
} else {
    // create new appointment post
    // ...
    // the user has everything and click the button
    if ( !empty($already_reserved->posts) ) {
        $response="Sorry, this date is no longer available."; 
    } else {
    // the appointment is set on the database
    }
}

this is supposing that first there is a validation of the date and only after that the user / users press the button to confirm.
if the button has a specific function try to edit that function to make this validation before set the appointment on the database.