Experiment: auto tune keep alive interval

pull/171/head
M66B 6 years ago
parent 32583890f6
commit d5ea82631e

File diff suppressed because it is too large Load Diff

@ -167,7 +167,11 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
tvHost.setText(String.format("%s:%d", account.host, account.port)); tvHost.setText(String.format("%s:%d", account.host, account.port));
tvLast.setText(context.getString(R.string.title_last_connected, tvLast.setText(context.getString(R.string.title_last_connected,
account.last_connected == null ? "-" : DTF.format(account.last_connected))); (account.last_connected == null ? "-" : DTF.format(account.last_connected)) +
(BuildConfig.DEBUG ?
" " + account.poll_interval +
"/" + account.keep_alive_ok +
"/" + account.keep_alive_failed : "")));
tvIdentity.setVisibility(account.identities > 0 || !settings ? View.GONE : View.VISIBLE); tvIdentity.setVisibility(account.identities > 0 || !settings ? View.GONE : View.VISIBLE);
tvDrafts.setVisibility(account.drafts || !settings ? View.GONE : View.VISIBLE); tvDrafts.setVisibility(account.drafts || !settings ? View.GONE : View.VISIBLE);

@ -3201,6 +3201,7 @@ class Core {
private Semaphore semaphore = new Semaphore(0); private Semaphore semaphore = new Semaphore(0);
private boolean running = true; private boolean running = true;
private boolean recoverable = true; private boolean recoverable = true;
private Long lastActivity = null;
State(ConnectionHelper.NetworkState networkState) { State(ConnectionHelper.NetworkState networkState) {
this.networkState = networkState; this.networkState = networkState;
@ -3264,6 +3265,7 @@ class Core {
void reset() { void reset() {
recoverable = true; recoverable = true;
lastActivity = null;
} }
private void yield() { private void yield() {
@ -3308,10 +3310,20 @@ class Core {
} }
} }
synchronized void activity() {
lastActivity = SystemClock.elapsedRealtime();
}
long getIdleTime() {
return (lastActivity == null ? 0 : SystemClock.elapsedRealtime() - lastActivity);
}
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
return "[running=" + running + ",recoverable=" + recoverable + "]"; return "[running=" + running +
",recoverable=" + recoverable +
",activity=" + new Date(lastActivity == null ? 0 : lastActivity) + "]";
} }
} }
} }

@ -56,7 +56,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 126, version = 127,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -1224,6 +1224,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `message` ADD COLUMN `autocrypt` TEXT"); db.execSQL("ALTER TABLE `message` ADD COLUMN `autocrypt` TEXT");
} }
}) })
.addMigrations(new Migration(126, 127) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `keep_alive_ok` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `account` ADD COLUMN `keep_alive_failed` INTEGER NOT NULL DEFAULT 0");
}
})
.build(); .build();
} }

@ -121,6 +121,15 @@ public interface DaoAccount {
@Query("UPDATE account SET last_connected = :last_connected WHERE id = :id") @Query("UPDATE account SET last_connected = :last_connected WHERE id = :id")
int setAccountConnected(long id, long last_connected); int setAccountConnected(long id, long last_connected);
@Query("UPDATE account SET poll_interval = :value WHERE id = :id")
int setAccountKeepAliveInterval(long id, int value);
@Query("UPDATE account SET keep_alive_ok = :ok WHERE id = :id")
int setAccountKeepAliveOk(long id, boolean ok);
@Query("UPDATE account SET keep_alive_failed = :value WHERE id = :id")
int setAccountKeepAliveFailed(long id, int value);
@Query("UPDATE account SET `order` = :order WHERE id = :id") @Query("UPDATE account SET `order` = :order WHERE id = :id")
int setAccountOrder(long id, Integer order); int setAccountOrder(long id, Integer order);

@ -101,7 +101,11 @@ public class EntityAccount extends EntityOrder implements Serializable {
public Long swipe_right; public Long swipe_right;
public Long move_to; public Long move_to;
@NonNull @NonNull
public Integer poll_interval = DEFAULT_KEEP_ALIVE_INTERVAL; // keep-alive interval public Integer poll_interval = DEFAULT_KEEP_ALIVE_INTERVAL;
@NonNull
public Boolean keep_alive_ok = false;
@NonNull
public Integer keep_alive_failed = 0;
@NonNull @NonNull
public Boolean partial_fetch = true; public Boolean partial_fetch = true;
@NonNull @NonNull

@ -1020,6 +1020,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
while (ifolder.isOpen() && state.isRunning() && state.isRecoverable()) { while (ifolder.isOpen() && state.isRunning() && state.isRecoverable()) {
Log.i(folder.name + " do idle"); Log.i(folder.name + " do idle");
ifolder.idle(false); ifolder.idle(false);
state.activity();
} }
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(folder.name, ex); Log.e(folder.name, ex);
@ -1144,23 +1145,51 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
// Keep alive // Keep alive
boolean first = true;
while (state.isRunning()) { while (state.isRunning()) {
if (!state.isRecoverable()) try {
throw new StoreClosedException(iservice.getStore(), "Unrecoverable"); if (!state.isRecoverable())
throw new StoreClosedException(iservice.getStore(), "Unrecoverable");
// Sends store NOOP
if (!iservice.getStore().isConnected()) // Sends store NOOP
throw new StoreClosedException(iservice.getStore(), "NOOP"); if (!iservice.getStore().isConnected())
throw new StoreClosedException(iservice.getStore(), "NOOP");
if (sync)
for (EntityFolder folder : mapFolders.keySet()) if (sync)
if (folder.synchronize) for (EntityFolder folder : mapFolders.keySet())
if (!folder.poll && capIdle) { if (folder.synchronize)
// Sends folder NOOP if (!folder.poll && capIdle) {
if (!mapFolders.get(folder).isOpen()) // Sends folder NOOP
throw new StoreClosedException(iservice.getStore(), folder.name); if (!mapFolders.get(folder).isOpen())
} else throw new StoreClosedException(iservice.getStore(), folder.name);
EntityOperation.sync(this, folder.id, false); } else
EntityOperation.sync(this, folder.id, false);
} catch (Throwable ex) {
if (!first && !account.keep_alive_ok &&
account.poll_interval > 9 &&
state.getIdleTime() > (account.poll_interval - 1) * 60 * 1000L) {
account.keep_alive_failed++;
if (account.keep_alive_failed > 10) {
account.keep_alive_failed = 0;
account.poll_interval--;
db.account().setAccountKeepAliveInterval(account.id, account.poll_interval);
}
db.account().setAccountKeepAliveFailed(account.id, account.keep_alive_failed);
EntityLog.log(ServiceSynchronize.this, account.name +
" keep alive failed=" + account.keep_alive_failed +
" keep alive interval=" + account.poll_interval +
" max idle=" + state.getIdleTime());
}
throw ex;
}
if (!first && !account.keep_alive_ok &&
account.poll_interval > 9 &&
state.getIdleTime() > (account.poll_interval - 1) * 60 * 1000L) {
account.keep_alive_ok = true;
db.account().setAccountKeepAliveOk(account.id, true);
EntityLog.log(ServiceSynchronize.this, account.name + " keep alive ok");
}
// Successfully connected: reset back off time // Successfully connected: reset back off time
state.setBackoff(CONNECT_BACKOFF_START); state.setBackoff(CONNECT_BACKOFF_START);
@ -1201,6 +1230,8 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
} finally { } finally {
am.cancel(pi); am.cancel(pi);
} }
first = false;
} }
Log.i(account.name + " done state=" + state); Log.i(account.name + " done state=" + state);

Loading…
Cancel
Save