Microsoft Graph API (Calendar) Intermittent 503 Errors

Viewed 506

This code sometimes works, but frequently runs for ~20s then fails with the "503 Service Unavailable" message when I call getPage(). The authentication/token request seems to be working fine.

I can't seem to identify any pattern of when it fails/succeeds. I don't believe it's a throttling error, as there is no "Retry-After" header returned, and the script only runs once per day at night with <200 records returned. I've also tried removing the $filter and changing parameter order as described here, with no clear benefit.

Can someone please help find the cause here? Happy to share any additional info. Any help is much appreciated, thanks!

<?php
define('DEBUG', true);
session_start();

// set config vars
$ms_url_base = "https://login.microsoftonline.com/d3523db7-f84a-4a24-a815-cd4ba4691c9c";
$ms_client_id = '<client id>';
$ms_redirect_uri = "https://example.com/path";
$ms_scope = "calendars.readwrite user.read";
$ms_client_secret = '<secret>';
$ms_auth_endpoint = '/oauth2/v2.0/authorize';
$ms_token_endpoint = '/oauth2/v2.0/token';
$query_numdays = 100;
if (DEBUG) error_reporting(E_ALL);
date_default_timezone_set("America/Detroit");

require_once __DIR__.'/vendor/autoload.php';

use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;
class EventMod extends \Microsoft\Graph\Model\Event {
    // custom functions here 

    public function getNextLink() {
        parent::getNextLink();
    }
}
class ResponseMod extends \Microsoft\Graph\Http\GraphResponse {}

// authorization
$provider = new Stevenmaguire\OAuth2\Client\Provider\Microsoft([
    'clientId'                  => $ms_client_id,
    'clientSecret'              => $ms_client_secret,
    'redirectUri'               => $ms_redirect_uri,
    'urlAuthorize'              => $ms_url_base.$ms_auth_endpoint,
    'urlAccessToken'            => $ms_url_base.$ms_token_endpoint,
    'urlResourceOwnerDetails'   => 'https://graph.microsoft.com/v2.0/me',
]);

if (!isset($_GET['code'])) {
    $options = ['scope' => $ms_scope, 'aud' => 'Graph'];
    $authUrl = $provider->getAuthorizationUrl($options);
    $_SESSION['oauth2state'] = $provider->getState();
    header('Location: '.$authUrl);
    exit;

} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
    unset($_SESSION['oauth2state']);
    exit('Invalid state');
} else {
    try {
        $token = $provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
    } catch (League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
        exit ("Get access token exception: ".$e->getMessage());
    }

    if (DEBUG) {
        echo 'Access Token: ' . $token->getToken() . "<p>";
        echo 'Refresh Token: ' . $token->getRefreshToken() . "<p>";
        echo 'Expired in: ' . $token->getExpires() . "<p>";
        echo 'Already expired? ' . ($token->hasExpired() ? 'expired' : 'not expired') . "<p>";
    }

    // start calendar query
    $start=new DateTimeImmutable("yesterday 0:0:1");
    $end = $start->add(new DateInterval("P".$query_numdays."D"));

    $url='/me/calendarview'
        .'?startdatetime='.$start->format('c')
        .'&enddatetime='.$end->format('c')
        .'&$filter=isOrganizer+eq+false'
        .'&$select=subject,responseStatus,start,categories'
        .'&$orderby=start/dateTime';

    $graph = new Graph;
    $graph->setAccessToken($token->getToken());
    $data = array();
    try {
        $iterator = $graph->createCollectionRequest("GET", $url)
                    ->setReturnType(EventMod::class)
                    ->addHeaders(["Prefer" => 'outlook.timezone="America/Detroit"'])
                    ->setPageSize(25);
        do {
            $page = $iterator->getPage(); /*************** THIS IS WHERE THE EXCEPTION HAPPENS ************/
            if (DEBUG) echo "<pre>".print_r($page, true)."</pre>";
            $data = array_merge($data, $page);
        } while (!$iterator->isEnd());
    } 
    catch (\Microsoft\Graph\Exception\GraphException $e) {
        if (DEBUG) echo "GraphException Message: ".$e->getMessage();
        exit;
    }
    catch (Exception $e) {
        if (DEBUG) echo "Unk Exception getting data: ".$e->getMessage();
        exit;
    }

    if (DEBUG) print_r($data);
}
?>

composer.json

{
    "require": {
        "microsoft/microsoft-graph": "^1.29",
        "stevenmaguire/oauth2-microsoft": "^2.2"
    }
}
2 Answers

I played with your above API call and i ended up noticing the issue - in the case of having bigger larger dates and lot of data at my end (not with smaller time window or less records). It tells me that the failure is due to the client timeout. We need to understand that Calendar view is an expensive operation that too when you deal with calendars and filters added to it. So i went ahead in this scenario, reduce/minimize time window for calendar view by client so smaller segments of time are scanned for matching calendar events. It helped me to get the records as i expected and make use of effective usage of Calendarview API call too.

Apparently the issue was not in the code at all. I discovered that the 503 error was thrown only when a specific date's events were being read by the API. One of the events was rather large and I deleted it; after this the script works well.

This particular event has 16536 attendees listed, which I believe was the source of the error. Still unexplained is why the error was intermittent. I was eventually able to get graph explorer to successfully read the event, so perhaps the bug is in the Microsoft Graph SDK for PHP. I will post there to see if the devs want to capture this error condition.

Related