package io.fathom.auto.haproxy;
import io.fathom.auto.config.ConfigEntry;
import io.fathom.auto.config.ConfigPath;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public abstract class ConfigSyncBase implements ConfigSync {
private static final Logger log = LoggerFactory.getLogger(ConfigSyncBase.class);
// TODO: Abstract out
final ConfigPath base;
final Map<String, Record> records;
public ConfigSyncBase(ConfigPath base) {
this.base = base;
this.records = Maps.newHashMap();
}
public static class Record {
final String name;
long lastModified;
public Record(String name, long lastModified) {
this.name = name;
this.lastModified = lastModified;
}
}
public synchronized int refresh() throws IOException {
Set<String> found = Sets.newHashSet();
List<Record> changed = Lists.newArrayList();
// TODO: Make multithreaded??
for (ConfigEntry o : base.listChildren()) {
long time = o.getVersion();
String name = o.getName();
Record record = records.get(name);
if (record == null) {
log.debug("Adding record: {}", name);
record = new Record(name, time);
changed.add(record);
records.put(name, new Record(name, time));
} else {
found.add(record.name);
if (record.lastModified != time) {
log.debug("Updating record: {}", name);
changed.add(record);
}
}
}
List<Record> delete = Lists.newArrayList();
for (Record record : records.values()) {
if (!found.contains(record.name)) {
log.debug("Deleting zone: {}", record.name);
delete.add(record);
records.remove(record.name);
}
}
return synchronizeChanges(changed, delete);
}
public synchronized int firstSync() throws IOException {
int failures = 0;
// TODO: Make multithreaded??
for (ConfigEntry o : base.listChildren()) {
long time = o.getVersion();
String name = o.getName();
try {
Record record = new Record(name, time);
if (updateRecord(record)) {
records.put(name, record);
} else {
// This isn't actually a failure; it means the record didn't
// exist when we went to read it
failures++;
}
} catch (Exception e) {
log.error("Error updating record: " + name, e);
failures++;
}
}
return failures;
}
protected int synchronizeChanges(Collection<Record> upsert, Collection<Record> delete) {
int failures = 0;
// TODO: Multithreaded?
for (Record record : upsert) {
try {
updateRecord(record);
} catch (Exception e) {
log.error("Error updating record: " + record.name, e);
failures++;
}
}
for (Record record : delete) {
try {
deleteRecord(record);
} catch (Exception e) {
log.error("Error inserting record: " + record.name, e);
failures++;
}
}
return failures;
}
protected abstract boolean updateRecord(Record record) throws SQLException, IOException;
protected abstract boolean deleteRecord(Record record) throws SQLException, IOException;
}