protected function executeOperation(LdapOperationInterface $operation, LogOperation $log = null)
    {
        $lastServer = $this->connection->getServer();
        try {
            $this->connectIfNotBound($operation);
            $lastServer = $this->connection->getServer();
            $handler = $this->getOperationHandler($operation);
            $handler->setOperationDefaults($operation);
            $this->logStart($log);
            $this->invalidateCacheIfNeeded($operation);
            list($result, $usedCache, $cacheKey) = $this->getCacheOrHandlerResult($operation, $handler, $log);
            $this->cacheResultIfNeeded($operation, $result, $usedCache, $cacheKey);

            return $result;
        } catch (\Throwable $e) {
            $this->logExceptionAndThrow($e, $log);
        } catch (\Exception $e) {
            $this->logExceptionAndThrow($e, $log);
        } finally {
            $this->logEnd($log);
            // It would not have set controls or switched servers if the cache was used...
            if (isset($usedCache) && $usedCache === false) {
                $this->resetLdapControls($operation);
                $this->switchServerIfNeeded($this->connection->getServer(), $lastServer, $operation);
            }
            $this->dispatcher->dispatch(new LdapOperationEvent(Event::LDAP_OPERATION_EXECUTE_AFTER, $operation, $this->connection));
        }
    }