views:

634

answers:

3

Hey everyone, I am using the Zend_GData Package to connect to a Google Calendar and do various things such as add/edit/delete events. However, I find that these are pretty expensive operations, and can sometimes take a visible 4 seconds to complete. I was wondering if there is anything I can do to optimize my code and make it faster? Here is an example of my add function:

function add_gcal($title, $date, $where, $desc){

$newIncludePath = array();
$newIncludePath[] = '../ZendGdata-1.8.4PL1/library';
$newIncludePath = implode($newIncludePath);
set_include_path($newIncludePath);

  // load classes
  require_once 'Zend/Loader.php';
  Zend_Loader::loadClass('Zend_Gdata');
  Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  Zend_Loader::loadClass('Zend_Gdata_Calendar');
  Zend_Loader::loadClass('Zend_Http_Client');

  // connect to service
  $user = "******@gmail.com";
  $pass = "*******";
  $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);

try {
 $gdataCal = new Zend_Gdata_Calendar($client);
 $newEvent = $gdataCal->newEventEntry();

 $newEvent->title = $gdataCal->newTitle($title);
 $newEvent->where = array($gdataCal->newWhere($where));
 $newEvent->content = $gdataCal->newContent("$desc");

 $when = $gdataCal->newWhen();
 $when->startTime = $date;
 $when->endTime = $date; 
 $newEvent->when = array($when);

 $createdEvent = $gdataCal->insertEvent($newEvent);
 return $createdEvent->id->text;

  } catch (Zend_Gdata_App_Exception $e) {
  return NULL;
  }

}

Does anyone see if I can make any improvements? For each function (add/delete/edit), I have to do something similar to this, where I load the classes and connect to the service...

Thanks!

EDIT:

I tried setting the $client connection once upon login, and storing $client as a SESSION variable that I could then re-use each time I need to do an operation on the calendar. This doesn't seem to work, I get the following Zend_GData_App_Exception:

exception 'Zend_Gdata_App_HttpException' with message 'Argument is not an instance of Zend_Http_Client.' 
    in /home/content/b/e/h/behrk2/html/ZendGdata-1.8.4PL1/library/Zend/Gdata/App.php:247 
Stack trace: 
    #0 /home/content/b/e/h/behrk2/html/ZendGdata-1.8.4PL1/library/Zend/Gdata/App.php(172): 
        Zend_Gdata_App->setHttpClient(Object(__PHP_Incomplete_Class), 'MyCompany-MyApp...') 
    #1 /home/content/b/e/h/behrk2/html/ZendGdata-1.8.4PL1/library/Zend/Gdata.php(107): 
        Zend_Gdata_App->__construct(Object(__PHP_Incomplete_Class), 'MyCompany-MyApp...') 
    #2 /home/content/b/e/h/behrk2/html/ZendGdata-1.8.4PL1/library/Zend/Gdata/Calendar.php(87): 
        Zend_Gdata->__construct(Object(__PHP_Incomplete_Class), 'MyCompany-MyApp...') 
    #3 /home/content/b/e/h/behrk2/html/scripts/functions.php(85): 
        Zend_Gdata_Calendar->__construct(Object(__PHP_Incomplete_Class)) 
    #4 /home/content/b/e/h/behrk2/html/calendar/modify_duty.php(68): 
        add_gcal(Object(__PHP_Incomplete_Class), 'R4 - Stephen Au...', '2009-08-19', 'Building: Liva ...', 'Primary') 
    #5 {main}

I also tried using this:

  // connect to service
  $user = "[email protected]";
  $pass = "154Kmb";
  $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);

  $client->setConfig(array('keepalive' => true));

I get no errors here, but it's still slow. I am not doing this upon login and setting a SESSION variable, like I did above, as I got the same error. While this gets no error, it seems to me like it would still make a new connection each time...How would I use this exactly?

+1  A: 

Hi,

I don't really know the GData components, but looking at your function, here's a thing :

It seems you are using Zend_Gdata_ClientLogin::getHttpClient each time you are creating an event ; if this method has to connect to Google to identify you, it might take some time.
Taking a look at the source of this method (in an old version of ZF), it seems it indeed does a query to google's server)

This means, to create an event, you have at least to client-server trips :

  • one for the authentication of the user
  • one for the creation of the event

Those HTTP requests are probably what's taking the most time in your application.
So, here's an idea -- the goal being to minimize the number of HTTP requests :

  • If you are always creating events for the same user, could you not do this only once ? And re-use the $client object for each event, instead of re-creating it ?
  • If you are creating events for several users, maybe you can do it user by user, so if a user has several events to create, those are created immediatly one after the other, using the same $client ?


A nice thing would have been the ability to insert several events with only one HTTP request... But it doesn't seem to be possible :-(


Another idea, if you have LOTS of events to create would be to use some batch program :

  • Your web application would only insert the events in a DB local to your server (not directly send them to Google) => really fast
  • You'd have another script, that runs in the background (launched every couple of minutes via cron, for instance), that would send those to Google (and mark them as "sent" in your DB so they're not sent twice or more)
    • that script could even be launched several times in parallel, to send more events to Google in the same amount of time...

But this is probably overkill if you don't need to create, literraly, tons of events...


The loading of the classes doesn't cost much : it is really negligible compared to the round-trip to Google's server you need to have to create the event.
Still, you could move the require_once calls outside of the function : it'd make it shorter ;-)
You could also use Zend Framework's autoloading mecanism, so you don't ever have to write those :-)

Pascal MARTIN
Pascal MARTIN - thanks so much for your help. I connect to the google calendar on several of my PHP pages. Is it possible to reuse the same $client throughout the whole session? For instance, would the best way be to make the connection on Login, and then store that $client as a SESSION variable?A batch program would be nice but, like you said, I don't have a ton of events so it's probably not worth it.I am also going to look into the Zend autoloader for the require_once calls. Thanks!
behrk2
To be honnest, I do not know if the $client variable could be stored in session ; you'll have to try that... (Just post the result of your test ;-) it might interest other people ^^ ) -- well, look at http://framework.zend.com/manual/en/zend.gdata.authsub.html : there is an example where the session token is stored into $_SESSION ; maybe that could help you :-)
Pascal MARTIN
I don't think I can do that, see my EDIT above. So, I guess the only thing I can look into is the keep-alive, but after reading about it, I'm not sure if and how I could use it - http://framework.zend.com/manual/en/zend.http.html
behrk2
+1  A: 

You could try turning keepalive on inside the Http_Client (the Gdata bits have a method for passing in your own instance, so you can configure how you like) and have it reuse the same one for multiple requests.

Or, modify/replace the Zend_Gdata components with something of your own that supports the batch-operations the API supports:

Greg
Greg, what's the difference (or, advantages/disadvantages) to using keep-alive versus using the same $client for each call?
behrk2
I tried using keepalive, please see me EDIT. I'm not sure when to use it...
behrk2
You have to use the same client in order for keepalive to actually do anything, it won't be in effect using different clients, see bottom of http://framework.zend.com/manual/en/zend.http.client.advanced.html
Greg
A: 

I was able to fix the issue by storing the following into a SESSION:

    $serviceName = Zend_Gdata_Calendar::AUTH_SERVICE_NAME; // predefined service name ('cl') for calendar
 $applicationName = 'yourCompany-yourAppName-v1';

 // Create an authenticated HTTP client
 $httpClient = Zend_Gdata_ClientLogin::getHttpClient('*****@gmail.com', '*****', $serviceName, null, $applicationName);
 $client = new Zend_Gdata_Calendar($httpClient, $applicationName); // Create an instance of the Calendar service

 $_SESSION['gdataCal'] = $client;

Hopefully this will speed things up!

Thanks all for you help.

behrk2
You're welcome :-) It won't make things perfect : you're calling an external service, and that is always sloooooower than we'd like ^^ But maybe it will help at least a bit...
Pascal MARTIN
Earlier today, my site seemed to be faster (with operations on the calendar). Tonight, its pretty slow :( - Sometimes I can delete an event in 2 seconds, but sometimes it would take 10 seconds to add an event!!!! Speed is important to me, because some of my users will be, at the beginning of every month, plugging in calendar data for a whole month!I just found out that GoDaddy has a Cron Manager for my Linux Shared Hosting. I'm going to try the "batch add/delete/update" route, where every hour, anything flagged as new in my DB table will silently run...
behrk2