diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragment.kt
index 9cd82d33ff273c4a9fedc9090decfbb7b49fef64..fdf47d9117dec853facb2077323e33a4564edd1f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragment.kt
@@ -22,6 +22,8 @@ package de.kuschku.quasseldroid.ui.clientsettings.client
 import android.content.SharedPreferences
 import android.os.Build
 import android.os.Bundle
+import android.os.Handler
+import android.os.HandlerThread
 import android.view.Menu
 import android.view.MenuInflater
 import android.view.MenuItem
@@ -29,6 +31,7 @@ import androidx.preference.ListPreference
 import androidx.preference.Preference
 import androidx.preference.PreferenceGroup
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.persistence.db.QuasselDatabase
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.Settings
 import de.kuschku.quasseldroid.ui.clientsettings.about.AboutActivity
@@ -38,14 +41,26 @@ import de.kuschku.quasseldroid.util.ui.settings.DaggerPreferenceFragmentCompat
 import javax.inject.Inject
 
 class ClientSettingsFragment : DaggerPreferenceFragmentCompat(),
-                               SharedPreferences.OnSharedPreferenceChangeListener {
+  SharedPreferences.OnSharedPreferenceChangeListener {
 
   @Inject
   lateinit var appearanceSettings: AppearanceSettings
 
+  private lateinit var handlerThread: HandlerThread
+  private lateinit var handler: Handler
+
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setHasOptionsMenu(true)
+
+    handlerThread = HandlerThread("ClientSettings")
+    handlerThread.start()
+    handler = Handler(handlerThread.looper)
+  }
+
+  override fun onDestroy() {
+    super.onDestroy()
+    handlerThread.quit()
   }
 
   override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -57,6 +72,14 @@ class ClientSettingsFragment : DaggerPreferenceFragmentCompat(),
     } else {
       findPreference(getString(R.string.preference_notification_configure_key)).isVisible = false
     }
+    findPreference(getString(R.string.preference_clear_cache_key)).setOnPreferenceClickListener {
+      activity?.let {
+        handler.post {
+          QuasselDatabase.Creator.init(it).message().clearMessages()
+        }
+      }
+      true
+    }
   }
 
   override fun onStart() {
@@ -74,7 +97,7 @@ class ClientSettingsFragment : DaggerPreferenceFragmentCompat(),
     updateSummary(findPreference(key))
     val appearanceSettings = Settings.appearance(context!!)
     if (this.appearanceSettings.theme != appearanceSettings.theme ||
-        this.appearanceSettings.language != appearanceSettings.language) {
+      this.appearanceSettings.language != appearanceSettings.language) {
       activity?.recreate()
     }
   }
@@ -103,14 +126,14 @@ class ClientSettingsFragment : DaggerPreferenceFragmentCompat(),
       WhitelistActivity.launch(requireContext())
       true
     }
-    R.id.action_crashes      -> {
+    R.id.action_crashes -> {
       CrashActivity.launch(requireContext())
       true
     }
-    R.id.action_about        -> {
+    R.id.action_about -> {
       AboutActivity.launch(requireContext())
       true
     }
-    else                     -> super.onOptionsItemSelected(item)
+    else -> super.onOptionsItemSelected(item)
   }
 }
diff --git a/app/src/main/res/values/strings_preferences.xml b/app/src/main/res/values/strings_preferences.xml
index 0eb4239803abc884ec7a65d6c6de249b79cd2674..40bbb3a273726a235cf1bfe4eb58ba9f139d02fb 100644
--- a/app/src/main/res/values/strings_preferences.xml
+++ b/app/src/main/res/values/strings_preferences.xml
@@ -299,6 +299,8 @@
   <string name="preference_initial_amount_title">Initial Amount</string>
   <string name="preference_initial_amount_summary">Number of messages to load when opening a buffer for the first time</string>
 
+  <string name="preference_clear_cache_key" translatable="false">clear_cache_key</string>
+  <string name="preference_clear_cache_title">Clear Backlog Cache</string>
 
   <string name="preference_connection_title">Connection</string>
 
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index ef32d4780c6c3453ad776519df5206017ce53c8b..5168d9b00a5883eeb73b8cb5e3de3f63d1c32683 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -306,6 +306,9 @@
       android:key="@string/preference_initial_amount_key"
       android:summary="@string/preference_initial_amount_summary"
       android:title="@string/preference_initial_amount_title" />
+    <PreferenceScreen
+      android:key="@string/preference_clear_cache_key"
+      android:title="@string/preference_clear_cache_title" />
   </PreferenceCategory>
 
   <PreferenceCategory android:layout="@layout/widget_preference_divider" />
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
index 423c811a13f1967056acafe0e71a56013f2223f2..ce3b5b66cae74097e9a714532adf4bd411e5e156 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
@@ -17,6 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 @file:Suppress("NOTHING_TO_INLINE")
+
 package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
@@ -79,9 +80,11 @@ class IrcChannel(
     "D" to QVariant.of(_D_channelModes.joinToString(), Type.QString)
   )
 
-  override fun initUserModes(): QVariantMap = _userModes.entries.map { (key, value) ->
-    key.nick() to QVariant.of(value, Type.QString)
-  }.toMap()
+  override fun initUserModes(): QVariantMap = synchronized(_userModes) {
+    _userModes.entries.map { (key, value) ->
+      key.nick() to QVariant.of(value, Type.QString)
+    }.toMap()
+  }
 
   override fun initProperties(): QVariantMap = mapOf(
     "name" to QVariant.of(name(), Type.QString),
@@ -120,8 +123,8 @@ class IrcChannel(
     setEncrypted(properties["encrypted"].indexed(i).valueOr(this::encrypted))
   }
 
-  fun isKnownUser(ircUser: IrcUser): Boolean {
-    return _userModes.contains(ircUser)
+  fun isKnownUser(ircUser: IrcUser) = synchronized(_userModes) {
+    _userModes.contains(ircUser)
   }
 
   fun isValidChannelUserMode(mode: String): Boolean {
@@ -133,15 +136,23 @@ class IrcChannel(
   fun password() = _password
   fun encrypted() = _encrypted
   fun network() = _network
-  fun ircUsers() = _userModes.keys
-  fun liveIrcUsers(): Observable<MutableSet<IrcUser>> =
-    live_userModes.map { _userModes }.map(MutableMap<IrcUser, String>::keys)
+  fun ircUsers() = synchronized(_userModes) {
+    _userModes.keys.toSet()
+  }
 
-  fun userModes(ircUser: IrcUser) = _userModes.getOr(ircUser, "")
-  fun liveUserModes(ircUser: IrcUser) = live_userModes.map {
+  fun liveIrcUsers(): Observable<Set<IrcUser>> =
+    live_userModes.map { ircUsers() }
+
+  fun userModes(ircUser: IrcUser) = synchronized(_userModes) {
     _userModes.getOr(ircUser, "")
   }
 
+  fun liveUserModes(ircUser: IrcUser) = live_userModes.map {
+    synchronized(_userModes) {
+      _userModes.getOr(ircUser, "")
+    }
+  }
+
   fun userCount() = _userCount
 
   fun userModes(): Map<IrcUser, String> = _userModes
@@ -159,7 +170,7 @@ class IrcChannel(
       _C_channelModes.contains(mode)
     INetwork.ChannelModeType.D_CHANMODE ->
       _D_channelModes.contains(mode)
-    else                                ->
+    else ->
       false
   }
 
@@ -168,14 +179,14 @@ class IrcChannel(
       _B_channelModes.getOr(mode, "")
     INetwork.ChannelModeType.C_CHANMODE ->
       _C_channelModes.getOr(mode, "")
-    else                                ->
+    else ->
       ""
   }
 
   fun modeValueList(mode: Char): Set<String> = when (network().channelModeType(mode)) {
     INetwork.ChannelModeType.A_CHANMODE ->
       _A_channelModes.getOrElse(mode, ::emptySet)
-    else                                ->
+    else ->
       emptySet()
   }
 
@@ -248,19 +259,21 @@ class IrcChannel(
   }
 
   private fun joinIrcUsersInternal(rawUsers: List<IrcUser>, rawModes: List<String>) {
-    val users = rawUsers.zip(rawModes)
-    val newNicks = users.filter { !_userModes.contains(it.first) }
-    val oldNicks = users.filter { _userModes.contains(it.first) }
-    for ((user, modes) in oldNicks) {
-      modes.forEach { mode ->
-        addUserMode(user, mode)
+    synchronized(_userModes) {
+      val users = rawUsers.zip(rawModes)
+      val newNicks = users.filter { !_userModes.contains(it.first) }
+      val oldNicks = users.filter { _userModes.contains(it.first) }
+      for ((user, modes) in oldNicks) {
+        modes.forEach { mode ->
+          addUserMode(user, mode)
+        }
       }
+      for ((user, modes) in newNicks) {
+        _userModes[user] = modes
+        user.joinChannel(this, true)
+      }
+      updateUsers()
     }
-    for ((user, modes) in newNicks) {
-      _userModes[user] = modes
-      user.joinChannel(this, true)
-    }
-    updateUsers()
   }
 
   override fun joinIrcUser(ircuser: IrcUser) {
@@ -268,21 +281,23 @@ class IrcChannel(
   }
 
   override fun part(ircuser: IrcUser?) {
-    if (ircuser == null)
-      return
-    if (!isKnownUser(ircuser))
-      return
-    _userModes.remove(ircuser)
-    ircuser.partChannel(this)
-    if (network().isMe(ircuser) || _userModes.isEmpty()) {
-      for (user in _userModes.keys) {
-        user.partChannel(this)
+    synchronized(_userModes) {
+      if (ircuser == null)
+        return
+      if (!isKnownUser(ircuser))
+        return
+      _userModes.remove(ircuser)
+      ircuser.partChannel(this)
+      if (network().isMe(ircuser) || _userModes.isEmpty()) {
+        for (user in _userModes.keys) {
+          user.partChannel(this)
+        }
+        _userModes.clear()
+        network().removeIrcChannel(this)
+        proxy.stopSynchronize(this)
       }
-      _userModes.clear()
-      network().removeIrcChannel(this)
-      proxy.stopSynchronize(this)
+      updateUsers()
     }
-    updateUsers()
   }
 
   override fun part(nick: String?) {
@@ -290,10 +305,12 @@ class IrcChannel(
   }
 
   override fun setUserModes(ircuser: IrcUser?, modes: String?) {
-    if (ircuser == null || !isKnownUser(ircuser))
-      return
-    _userModes[ircuser] = modes ?: ""
-    updateUsers()
+    synchronized(_userModes) {
+      if (ircuser == null || !isKnownUser(ircuser))
+        return
+      _userModes[ircuser] = modes ?: ""
+      updateUsers()
+    }
   }
 
   override fun setUserModes(nick: String?, modes: String?) {
@@ -305,13 +322,15 @@ class IrcChannel(
   }
 
   override fun addUserMode(ircuser: IrcUser?, mode: String?) {
-    val userMode = mode ?: ""
-    if (ircuser == null || !isKnownUser(ircuser) || !isValidChannelUserMode(userMode))
-      return
-    if (_userModes.getOr(ircuser, "").contains(userMode, ignoreCase = true))
-      return
-    _userModes[ircuser] = _userModes.getOr(ircuser, "") + userMode
-    updateUsers()
+    synchronized(_userModes) {
+      val userMode = mode ?: ""
+      if (ircuser == null || !isKnownUser(ircuser) || !isValidChannelUserMode(userMode))
+        return
+      if (_userModes.getOr(ircuser, "").contains(userMode, ignoreCase = true))
+        return
+      _userModes[ircuser] = _userModes.getOr(ircuser, "") + userMode
+      updateUsers()
+    }
   }
 
   override fun addUserMode(nick: String?, mode: String?) {
@@ -319,14 +338,16 @@ class IrcChannel(
   }
 
   override fun removeUserMode(ircuser: IrcUser?, mode: String?) {
-    val userMode = mode ?: ""
-    if (ircuser == null || !isKnownUser(ircuser) || !isValidChannelUserMode(userMode))
-      return
-    if (!_userModes.getOr(ircuser, "").contains(userMode, ignoreCase = true))
-      return
-    _userModes[ircuser] = _userModes.getOr(ircuser, "")
-      .replace(userMode, "", ignoreCase = true)
-    updateUsers()
+    synchronized(_userModes) {
+      val userMode = mode ?: ""
+      if (ircuser == null || !isKnownUser(ircuser) || !isValidChannelUserMode(userMode))
+        return
+      if (!_userModes.getOr(ircuser, "").contains(userMode, ignoreCase = true))
+        return
+      _userModes[ircuser] = _userModes.getOr(ircuser, "")
+        .replace(userMode, "", ignoreCase = true)
+      updateUsers()
+    }
   }
 
   override fun removeUserMode(nick: String?, mode: String?) {
@@ -335,13 +356,13 @@ class IrcChannel(
 
   override fun addChannelMode(mode: Char, value: String?) {
     when (network().channelModeType(mode)) {
-      INetwork.ChannelModeType.A_CHANMODE     ->
+      INetwork.ChannelModeType.A_CHANMODE ->
         _A_channelModes.getOrPut(mode, ::mutableSetOf).add(value!!)
-      INetwork.ChannelModeType.B_CHANMODE     ->
+      INetwork.ChannelModeType.B_CHANMODE ->
         _B_channelModes[mode] = value!!
-      INetwork.ChannelModeType.C_CHANMODE     ->
+      INetwork.ChannelModeType.C_CHANMODE ->
         _C_channelModes[mode] = value!!
-      INetwork.ChannelModeType.D_CHANMODE     ->
+      INetwork.ChannelModeType.D_CHANMODE ->
         _D_channelModes.add(mode)
       INetwork.ChannelModeType.NOT_A_CHANMODE ->
         throw IllegalArgumentException("Received invalid channel mode: $mode $value")
@@ -350,13 +371,13 @@ class IrcChannel(
 
   override fun removeChannelMode(mode: Char, value: String?) {
     when (network().channelModeType(mode)) {
-      INetwork.ChannelModeType.A_CHANMODE     ->
+      INetwork.ChannelModeType.A_CHANMODE ->
         _A_channelModes.getOrPut(mode, ::mutableSetOf).remove(value)
-      INetwork.ChannelModeType.B_CHANMODE     ->
+      INetwork.ChannelModeType.B_CHANMODE ->
         _B_channelModes.remove(mode)
-      INetwork.ChannelModeType.C_CHANMODE     ->
+      INetwork.ChannelModeType.C_CHANMODE ->
         _C_channelModes.remove(mode)
-      INetwork.ChannelModeType.D_CHANMODE     ->
+      INetwork.ChannelModeType.D_CHANMODE ->
         _D_channelModes.remove(mode)
       INetwork.ChannelModeType.NOT_A_CHANMODE ->
         throw IllegalArgumentException("Received invalid channel mode: $mode $value")
@@ -398,7 +419,7 @@ class IrcChannel(
       live_updates.onNext(Unit)
     }
 
-  private fun updateUsers() {
+  private fun updateUsers() = synchronized(_userModes) {
     _userCount = _userModes.size
     live_userModes.onNext(Unit)
   }