public function recordEvent()
 {
     if ($this->getID()) {
         throw new Exception("Event has already been recorded!");
     }
     // Save the data first and point to it from the event to avoid a race
     // condition where we insert the event before the data and a consumer reads
     // it immediately.
     if ($this->data !== null) {
         $data = new PhabricatorTimelineEventData();
         $data->setEventData($this->data);
         $data->save();
         $this->setDataID($data->getID());
     }
     $this->save();
 }
 protected function loadEvents()
 {
     if (!$this->cursor) {
         $this->cursor = id(new PhabricatorTimelineCursor())->loadOneWhere('name = %s', $this->cursorName);
         if (!$this->cursor) {
             $cursor = new PhabricatorTimelineCursor();
             $cursor->setName($this->cursorName);
             $cursor->setPosition(0);
             $cursor->save();
             $this->cursor = $cursor;
         }
     }
     $event = new PhabricatorTimelineEvent('NULL');
     $event_data = new PhabricatorTimelineEventData();
     $raw_data = queryfx_all($event->establishConnection('r'), 'SELECT event.*, event_data.eventData eventData
     FROM %T event
     LEFT JOIN %T event_data ON event_data.id = event.dataID
     WHERE event.id > %d AND event.type in (%Ls)
     ORDER BY event.id ASC LIMIT %d', $event->getTableName(), $event_data->getTableName(), $this->cursor->getPosition(), $this->eventTypes, self::LOAD_CHUNK_SIZE);
     $events = $event->loadAllFromArray($raw_data);
     $events = mpull($events, null, 'getID');
     $raw_data = ipull($raw_data, 'eventData', 'id');
     foreach ($raw_data as $id => $data) {
         if ($data) {
             $decoded = json_decode($data, true);
             $events[$id]->setData($decoded);
         }
     }
     $this->events = $events;
     if ($this->events) {
         $this->events = array_values($this->events);
         $this->index = 0;
     } else {
         $this->cursor = null;
     }
 }