package org.zenoss.zep.dao.impl;
import com.google.common.collect.Lists;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcOperations;
import org.springframework.transaction.annotation.Transactional;
import org.zenoss.protobufs.zep.Zep.EventSummary;
import org.zenoss.zep.ZepException;
import org.zenoss.zep.annotations.TransactionalRollbackAllExceptions;
import org.zenoss.zep.dao.IndexQueueID;
import org.zenoss.zep.dao.impl.EventIndexQueueDaoImpl.PollEvents;
import org.zenoss.zep.dao.impl.compat.DatabaseCompatibility;
import org.zenoss.zep.dao.impl.compat.TypeConverter;
import javax.sql.DataSource;
import java.lang.reflect.Proxy;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ArchiveIndexDaoDelegate implements IndexDaoDelegate {
private final SimpleJdbcOperations template;
private final String queueTableName;
private final String tableName;
private final EventSummaryRowMapper rowMapper;
private final TypeConverter<String> uuidConverter;
private final TypeConverter<Long> timestampConverter;
public ArchiveIndexDaoDelegate(DataSource ds, EventDaoHelper daoHelper,
DatabaseCompatibility databaseCompatibility) {
this.template = (SimpleJdbcOperations) Proxy.newProxyInstance(SimpleJdbcOperations.class.getClassLoader(),
new Class<?>[]{SimpleJdbcOperations.class}, new SimpleJdbcTemplateProxy(ds));
this.tableName = EventConstants.TABLE_EVENT_ARCHIVE;
this.queueTableName = EventConstants.TABLE_EVENT_ARCHIVE + "_index_queue";
this.uuidConverter = databaseCompatibility.getUUIDConverter();
this.timestampConverter = databaseCompatibility.getTimestampConverter();
this.rowMapper = new EventSummaryRowMapper(daoHelper, databaseCompatibility);
}
@Override
public PollEvents pollEvents(int limit, long maxUpdateTime) {
return new PollArchiveIndexEvents(limit, maxUpdateTime);
}
@Override
@Transactional(readOnly = true)
public long getQueueLength() {
String sql = String.format("SELECT COUNT(*) FROM %s", queueTableName);
return template.queryForLong(sql);
}
@Override
@TransactionalRollbackAllExceptions
public void deleteIndexQueueIds(List<IndexQueueID> queueIds) throws ZepException {
if (queueIds.isEmpty()) {
return;
}
List<Long> ids = Lists.newArrayListWithCapacity(queueIds.size());
for (IndexQueueID id : queueIds) {
ids.add((Long) id.id);
}
final String deleteSql = "DELETE FROM " + this.queueTableName + " WHERE id IN (:_iq_ids)";
final Map<String, List<Long>> deleteFields = Collections.singletonMap("_iq_ids", ids);
this.template.update(deleteSql, deleteFields);
}
@Override
public String getQueueName() {
return queueTableName;
}
private class PollArchiveIndexEvents implements PollEvents {
private int limit;
private long maxUpdateTime;
private List<EventSummary> indexed;
private Set<String> deleted;
private List<Long> indexQueueIds;
public PollArchiveIndexEvents(int limit, long maxUpdateTime) {
this.limit = limit;
this.maxUpdateTime = maxUpdateTime;
}
public List<EventSummary> getIndexed() {
return indexed;
}
public Set<String> getDeleted() {
return deleted;
}
public List<IndexQueueID> getIndexQueueIds() {
ArrayList<IndexQueueID> result = Lists.newArrayListWithCapacity(indexQueueIds.size());
for (Long id : indexQueueIds) {
result.add(new IndexQueueID(id));
}
return result;
}
public PollArchiveIndexEvents invoke() {
final Map<String, Object> selectFields = new HashMap<String, Object>();
selectFields.put("_limit", limit);
final String sql;
// Used for partition pruning
final String queryJoinLastSeen = "AND iq.last_seen=es.last_seen ";
if (maxUpdateTime > 0L) {
selectFields.put("_max_update_time", timestampConverter.toDatabaseType(maxUpdateTime));
sql = "SELECT iq.id AS iq_id, iq.uuid AS iq_uuid, iq.update_time AS iq_update_time," +
"es.* FROM " + queueTableName + " AS iq " +
"LEFT JOIN " + tableName + " es ON iq.uuid=es.uuid " + queryJoinLastSeen +
"WHERE iq.update_time <= :_max_update_time " +
"ORDER BY iq_id LIMIT :_limit";
} else {
sql = "SELECT iq.id AS iq_id, iq.uuid AS iq_uuid, iq.update_time AS iq_update_time," +
"es.* FROM " + queueTableName + " AS iq " +
"LEFT JOIN " + tableName + " es ON iq.uuid=es.uuid " + queryJoinLastSeen +
"ORDER BY iq_id LIMIT :_limit";
}
final Set<String> eventUuids = new HashSet<String>();
indexed = new ArrayList<EventSummary>();
deleted = new HashSet<String>();
indexQueueIds = template.query(sql, new RowMapper<Long>() {
@Override
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
final long iqId = rs.getLong("iq_id");
final String iqUuid = uuidConverter.fromDatabaseType(rs, "iq_uuid");
// Don't process the same event multiple times.
if (eventUuids.add(iqUuid)) {
final Object uuid = rs.getObject("uuid");
if (uuid != null) {
indexed.add(rowMapper.mapRow(rs, rowNum));
} else {
deleted.add(iqUuid);
}
}
return iqId;
}
}, selectFields);
return this;
}
}
}