Skip to content
This repository has been archived by the owner on Aug 20, 2021. It is now read-only.

Commit

Permalink
feat: Improve performance of start/stop API
Browse files Browse the repository at this point in the history
  • Loading branch information
aelamrani authored and brasseld committed Jul 5, 2018
1 parent 23bdacd commit fda825e
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 145 deletions.
@@ -0,0 +1,101 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.management.model;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
* @author Azize ELAMRANI (azize.elamrani at graviteesource.com)
* @author GraviteeSource Team
*/
public class EventQuery {

private Collection<EventType> types;
private Map<String, Object> properties;
private long from, to;
private String api;

public Collection<EventType> getTypes() {
return types;
}

public void setTypes(Collection<EventType> types) {
this.types = types;
}

public Map<String, Object> getProperties() {
return properties;
}

public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}

public long getFrom() {
return from;
}

public void setFrom(long from) {
this.from = from;
}

public long getTo() {
return to;
}

public void setTo(long to) {
this.to = to;
}

public String getApi() {
return api;
}

public void setApi(String api) {
this.api = api;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EventQuery)) return false;
EventQuery query = (EventQuery) o;
return from == query.from &&
to == query.to &&
Objects.equals(types, query.types) &&
Objects.equals(properties, query.properties) &&
Objects.equals(api, query.api);
}

@Override
public int hashCode() {

return Objects.hash(types, properties, from, to, api);
}

@Override
public String toString() {
return "EventQuery{" +
"types=" + types +
", properties=" + properties +
", from=" + from +
", to=" + to +
", api='" + api + '\'' +
'}';
}
}
Expand Up @@ -17,6 +17,7 @@

import io.gravitee.common.http.MediaType;
import io.gravitee.management.model.EventEntity;
import io.gravitee.management.model.EventQuery;
import io.gravitee.management.model.permissions.RolePermission;
import io.gravitee.management.model.permissions.RolePermissionAction;
import io.gravitee.management.rest.resource.param.EventTypeListParam;
Expand Down Expand Up @@ -54,7 +55,9 @@ public class ApiEventsResource extends AbstractResource {
public List<EventEntity> events(
@PathParam("api") String api,
@ApiParam @DefaultValue("all") @QueryParam("type") EventTypeListParam eventTypeListParam) {
return eventService.findByApi(api).stream()
final EventQuery query = new EventQuery();
query.setApi(api);
return eventService.search(query).stream()
.filter(event -> eventTypeListParam.getEventTypes().contains(event.getType()))
.sorted((e1, e2) -> e2.getCreatedAt().compareTo(e1.getCreatedAt()))
.collect(Collectors.toList());
Expand Down
Expand Up @@ -17,12 +17,13 @@

import io.gravitee.common.data.domain.Page;
import io.gravitee.management.model.EventEntity;
import io.gravitee.management.model.EventQuery;
import io.gravitee.management.model.EventType;
import io.gravitee.management.model.NewEventEntity;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @author Titouan COMPIEGNE
Expand All @@ -37,14 +38,8 @@ public interface EventService {

void delete(String eventId);

Set<EventEntity> findByType(List<EventType> eventTypes);

Set<EventEntity> findByApi(String apiId);

Set<EventEntity> findByUser(String username);

Set<EventEntity> findByOrigin(String origin);

Page<EventEntity> search(
List<EventType> eventTypes, Map<String, Object> properties, long from, long to, int page, int size);

Collection<EventEntity> search(EventQuery query);
}
Expand Up @@ -63,6 +63,9 @@
import static io.gravitee.management.model.PageType.SWAGGER;
import static io.gravitee.repository.management.model.Api.AuditEvent.*;
import static java.util.stream.Collectors.toMap;
import static io.gravitee.management.model.EventType.PUBLISH_API;
import static java.util.Collections.singleton;
import static java.util.Comparator.comparing;

/**
* @author David BRASSELY (david.brassely at graviteesource.com)
Expand Down Expand Up @@ -134,8 +137,8 @@ public ApiEntity create(NewApiEntity newApiEntity, String userId) throws ApiAlre
Proxy proxy = new Proxy();
proxy.setContextPath(newApiEntity.getContextPath());
EndpointGroup group = new EndpointGroup();
group.setEndpoints(Collections.singleton(new HttpEndpoint("default", newApiEntity.getEndpoint())));
proxy.setGroups(Collections.singleton(group));
group.setEndpoints(singleton(new HttpEndpoint("default", newApiEntity.getEndpoint())));
proxy.setGroups(singleton(group));
apiEntity.setProxy(proxy);

List<String> declaredPaths = (newApiEntity.getPaths() != null) ? newApiEntity.getPaths() : new ArrayList<>();
Expand Down Expand Up @@ -410,7 +413,7 @@ public ApiEntity update(String apiId, UpdateApiEntity updateApiEntity) {
apiToUpdate,
updatedApi);

return convert(Collections.singleton(updatedApi), true).iterator().next();
return convert(singleton(updatedApi), true).iterator().next();
} else {
LOGGER.error("Unable to update API {} because of previous error.", api.getId());
throw new TechnicalManagementException("Unable to update API " + apiId);
Expand Down Expand Up @@ -454,7 +457,9 @@ public void delete(String apiId) {
}

// Delete events
eventService.findByApi(apiId)
final EventQuery query = new EventQuery();
query.setApi(apiId);
eventService.search(query)
.forEach(event -> eventService.delete(event.getId()));

// Delete API
Expand Down Expand Up @@ -524,7 +529,7 @@ public boolean isSynchronized(String apiId) {
properties.put(Event.EventProperties.API_ID.getValue(), apiId);

io.gravitee.common.data.domain.Page<EventEntity> events =
eventService.search(Arrays.asList(EventType.PUBLISH_API, EventType.UNPUBLISH_API),
eventService.search(Arrays.asList(PUBLISH_API, EventType.UNPUBLISH_API),
properties, 0, 0, 0, 1);

if (!events.getContent().isEmpty()) {
Expand Down Expand Up @@ -616,7 +621,7 @@ private ApiEntity deployCurrentAPI(String apiId, String userId, EventType eventT
// And create event
eventService.create(eventType, objectMapper.writeValueAsString(apiValue), properties);

return convert(Collections.singleton(apiValue), true).iterator().next();
return convert(singleton(apiValue), true).iterator().next();
} else {
throw new ApiNotFoundException(apiId);
}
Expand All @@ -631,10 +636,12 @@ private ApiEntity deployCurrentAPI(String apiId, String userId, EventType eventT
* @throws TechnicalException if an exception occurs while saving the API
*/
private ApiEntity deployLastPublishedAPI(String apiId, String userId, EventType eventType) throws TechnicalException {
Set<EventEntity> events = eventService.findByApi(apiId);
Optional<EventEntity> optEvent = events.stream()
.filter(event -> EventType.PUBLISH_API.equals(event.getType()))
.sorted((e1, e2) -> e2.getCreatedAt().compareTo(e1.getCreatedAt())).findFirst();
final EventQuery query = new EventQuery();
query.setApi(apiId);
query.setTypes(singleton(PUBLISH_API));

final Optional<EventEntity> optEvent =
eventService.search(query).stream().max(comparing(EventEntity::getCreatedAt));
try {
if (optEvent.isPresent()) {
EventEntity event = optEvent.get();
Expand All @@ -654,12 +661,9 @@ private ApiEntity deployLastPublishedAPI(String apiId, String userId, EventType
eventService.create(eventType, objectMapper.writeValueAsString(lastPublishedAPI), properties);
return null;
} else {
if (events.size() == 0) {
// this is the first time we start the api without previously deployed id.
// let's do it.
return this.deploy(apiId, userId, EventType.PUBLISH_API);
}
throw new TechnicalException("No event found for API " + apiId);
// this is the first time we start the api without previously deployed id.
// let's do it.
return this.deploy(apiId, userId, PUBLISH_API);
}
} catch (Exception e) {
LOGGER.error("An error occurs while trying to deploy last published API {}", apiId, e);
Expand Down
Expand Up @@ -17,10 +17,7 @@

import io.gravitee.common.data.domain.Page;
import io.gravitee.common.utils.UUID;
import io.gravitee.management.model.EventEntity;
import io.gravitee.management.model.EventType;
import io.gravitee.management.model.NewEventEntity;
import io.gravitee.management.model.UserEntity;
import io.gravitee.management.model.*;
import io.gravitee.management.service.EventService;
import io.gravitee.management.service.UserService;
import io.gravitee.management.service.exceptions.EventNotFoundException;
Expand All @@ -41,6 +38,10 @@
import java.util.*;
import java.util.stream.Collectors;

import static io.gravitee.repository.management.model.Event.EventProperties.API_ID;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.springframework.util.CollectionUtils.isEmpty;

/**
* @author Titouan COMPIEGNE
*/
Expand Down Expand Up @@ -120,30 +121,6 @@ public void delete(String eventId) {
}
}

@Override
public Set<EventEntity> findByType(List<EventType> eventTypes) {
io.gravitee.repository.management.model.EventType[] eventTypesArr = eventTypes.stream()
.map(eventType -> io.gravitee.repository.management.model.EventType.valueOf(eventType.toString()))
.toArray(io.gravitee.repository.management.model.EventType[]::new);

return convert(eventRepository.search(new EventCriteria.Builder().types(eventTypesArr).build()));
}

@Override
public Set<EventEntity> findByApi(String apiId) {
return findByProperty(Event.EventProperties.API_ID.getValue(), apiId);
}

@Override
public Set<EventEntity> findByUser(String username) {
return findByProperty(Event.EventProperties.USER.getValue(), username);
}

@Override
public Set<EventEntity> findByOrigin(String origin) {
return findByProperty(Event.EventProperties.ORIGIN.getValue(), origin);
}

private Set<EventEntity> findByProperty(String property, String value) {
return convert(eventRepository.search(new EventCriteria.Builder().property(property, value).build()));
}
Expand Down Expand Up @@ -174,6 +151,36 @@ public Page<EventEntity> search(List<EventType> eventTypes,
return new Page<>(content, page, size, pageEvent.getTotalElements());
}

@Override
public Collection<EventEntity> search(final EventQuery query) {
LOGGER.debug("Search APIs by {}", query);
return convert(eventRepository.search(queryToCriteria(query).build()));
}

private EventCriteria.Builder queryToCriteria(EventQuery query) {
final EventCriteria.Builder builder = new EventCriteria.Builder();
if (query == null) {
return builder;
}
builder
.from(query.getFrom())
.to(query.getTo());

if (!isEmpty(query.getTypes())) {
query.getTypes().forEach(eventType ->
builder.types(io.gravitee.repository.management.model.EventType.valueOf(eventType.name())));
}

if (!isEmpty(query.getProperties())) {
query.getProperties().forEach(builder::property);
}

if (!isBlank(query.getApi())) {
builder.property(API_ID.getValue(), query.getApi());
}
return builder;
}

private Set<EventEntity> convert(List<Event> events) {
return events.stream().map(this::convert).collect(Collectors.toSet());
}
Expand Down
Expand Up @@ -60,13 +60,13 @@ public class InstanceServiceImpl implements InstanceService {

@Override
public Collection<InstanceListItem> findInstances(boolean includeStopped) {
Set<EventEntity> events;

final EventQuery query = new EventQuery();
if (includeStopped) {
events = eventService.findByType(instancesAllState);
query.setTypes(instancesAllState);
} else {
events = eventService.findByType(instancesRunningOnly);
query.setTypes(instancesRunningOnly);
}
final Collection<EventEntity> events = eventService.search(query);

Instant nowMinusXMinutes = Instant.now().minus(5, ChronoUnit.MINUTES);
return events.stream().map(
Expand Down
Expand Up @@ -21,10 +21,8 @@
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import io.gravitee.definition.jackson.datatype.GraviteeMapper;
import io.gravitee.management.model.EventEntity;
import io.gravitee.management.model.*;
import io.gravitee.management.model.EventType;
import io.gravitee.management.model.PlanEntity;
import io.gravitee.management.model.UserEntity;
import io.gravitee.management.model.mixin.ApiMixin;
import io.gravitee.management.model.permissions.SystemRole;
import io.gravitee.management.service.exceptions.ApiNotFoundException;
Expand All @@ -46,6 +44,8 @@

import java.util.*;

import static io.gravitee.management.model.EventType.PUBLISH_API;
import static java.util.Collections.singleton;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

Expand Down Expand Up @@ -99,12 +99,15 @@ public void shouldStart() throws Exception {
objectMapper.addMixIn(Api.class, ApiMixin.class);
when(apiRepository.findById(API_ID)).thenReturn(Optional.of(api));
when(apiRepository.update(api)).thenReturn(api);
final EventEntity event = mockEvent(EventType.PUBLISH_API);
when(eventService.findByApi(API_ID)).thenReturn(Collections.singleton(event));
final EventEntity event = mockEvent(PUBLISH_API);
final EventQuery query = new EventQuery();
query.setApi(API_ID);
query.setTypes(singleton(PUBLISH_API));
when(eventService.search(query)).thenReturn(singleton(event));
Membership po = new Membership(USER_NAME, API_ID, MembershipReferenceType.API);
po.setRoles(Collections.singletonMap(RoleScope.API.getId(), SystemRole.PRIMARY_OWNER.name()));
when(membershipRepository.findByReferencesAndRole(any(), any(), any(), any()))
.thenReturn(Collections.singleton(po));
.thenReturn(singleton(po));

apiService.start(API_ID, USER_NAME);

Expand Down

0 comments on commit fda825e

Please sign in to comment.