Rework Fever synchronization to be more efficient

This commit is contained in:
Shinokuni 2024-08-07 14:45:44 +02:00
parent 732ae4efa4
commit 89c4dfad1a
4 changed files with 85 additions and 63 deletions

View File

@ -3,8 +3,11 @@ package com.readrops.api.services.fever
import com.readrops.api.services.SyncType import com.readrops.api.services.SyncType
import com.readrops.api.services.fever.adapters.FeverAPIAdapter import com.readrops.api.services.fever.adapters.FeverAPIAdapter
import com.readrops.api.utils.ApiUtils import com.readrops.api.utils.ApiUtils
import com.readrops.db.entities.Item
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import okhttp3.MultipartBody import okhttp3.MultipartBody
class FeverDataSource(private val service: FeverService) { class FeverDataSource(private val service: FeverService) {
@ -21,54 +24,68 @@ class FeverDataSource(private val service: FeverService) {
} }
suspend fun synchronize( suspend fun synchronize(
login: String,
password: String,
syncType: SyncType, syncType: SyncType,
syncData: FeverSyncData, syncData: FeverSyncData
body: MultipartBody ): FeverSyncResult = with(CoroutineScope(Dispatchers.IO)) {
): FeverSyncResult { val body = getFeverRequestBody(login, password)
if (syncType == SyncType.INITIAL_SYNC) { if (syncType == SyncType.INITIAL_SYNC) {
val unreadIds = service.getUnreadItemsIds(body) return FeverSyncResult().apply {
.reversed() listOf(
.subList(0, MAX_ITEMS_IDS) async { feverFeeds = service.getFeeds(body) },
async { folders = service.getFolders(body) },
async {
unreadIds = service.getUnreadItemsIds(body)
.reversed()
.subList(0, MAX_ITEMS_IDS)
var lastId = unreadIds.first() var lastId = unreadIds.first()
val items = arrayListOf<Item>() items = buildList {
repeat(INITIAL_SYNC_ITEMS_REQUESTS_COUNT) { repeat(INITIAL_SYNC_ITEMS_REQUESTS_COUNT) {
val newItems = service.getItems(body, lastId, null) val newItems = service.getItems(body, lastId, null)
lastId = newItems.last().remoteId!! lastId = newItems.last().remoteId!!
items += newItems addAll(newItems)
}
}
sinceId = unreadIds.first().toLong()
},
async { starredIds = service.getStarredItemsIds(body) },
async { favicons = listOf() }
)
.awaitAll()
} }
return FeverSyncResult(
feverFeeds = service.getFeeds(body),
folders = service.getFolders(body),
items = items,
unreadIds = unreadIds,
starredIds = service.getStarredItemsIds(body),
favicons = listOf(),
sinceId = unreadIds.first().toLong(),
)
} else { } else {
val items = arrayListOf<Item>() return FeverSyncResult().apply {
var sinceId = syncData.sinceId listOf(
async { folders = service.getFolders(body) },
async { feverFeeds = service.getFeeds(body) },
async { unreadIds = service.getUnreadItemsIds(body) },
async { starredIds = service.getStarredItemsIds(body) },
async { favicons = listOf() },
async {
items = buildList {
var sinceId = syncData.sinceId
while (true) { while (true) {
val newItems = service.getItems(body, null, sinceId) val newItems = service.getItems(body, null, sinceId)
if (newItems.isEmpty()) break if (newItems.isEmpty()) break
sinceId = newItems.first().remoteId!! sinceId = newItems.first().remoteId!!
items += newItems addAll(newItems)
}
}
if (items.isNotEmpty()) items.first().remoteId!!.toLong() else sinceId.toLong()
}
)
.awaitAll()
} }
return FeverSyncResult(
feverFeeds = service.getFeeds(body),
folders = service.getFolders(body),
items = items,
unreadIds = service.getUnreadItemsIds(body),
starredIds = service.getStarredItemsIds(body),
favicons = listOf(),
sinceId = if (items.isNotEmpty()) items.first().remoteId!!.toLong() else sinceId.toLong(),
)
} }
} }

View File

@ -6,11 +6,11 @@ import com.readrops.db.entities.Folder
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
data class FeverSyncResult( data class FeverSyncResult(
val feverFeeds: FeverFeeds, var feverFeeds: FeverFeeds = FeverFeeds(),
val folders: List<Folder>, var folders: List<Folder> = listOf(),
val items: List<Item>, var items: List<Item> = listOf(),
val unreadIds: List<String>, var unreadIds: List<String> = listOf(),
val starredIds: List<String>, var starredIds: List<String> = listOf(),
val favicons: List<Favicon>, var favicons: List<Favicon> = listOf(),
val sinceId: Long = 0, var sinceId: Long = 0,
) )

View File

@ -5,13 +5,14 @@ import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableString import com.readrops.api.utils.extensions.nextNullableString
import com.readrops.api.utils.extensions.skipField import com.readrops.api.utils.extensions.skipField
import com.readrops.api.utils.extensions.skipToEnd
import com.readrops.db.entities.Feed import com.readrops.db.entities.Feed
import com.squareup.moshi.* import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
data class FeverFeeds( data class FeverFeeds(
val feeds: List<Feed>, val feeds: List<Feed> = listOf(),
val feedsGroups: Map<Int, List<Int>> val feedsGroups: Map<Int, List<Int>> = emptyMap()
) )
class FeverFeedsAdapter : JsonAdapter<FeverFeeds>() { class FeverFeedsAdapter : JsonAdapter<FeverFeeds>() {

View File

@ -41,20 +41,24 @@ class FeverRepository(
} }
return feverDataSource.synchronize( return feverDataSource.synchronize(
account.login!!,
account.password!!,
syncType, syncType,
FeverSyncData(account.lastModified.toString()), FeverSyncData(account.lastModified.toString())
getFeverRequestBody()
).run { ).run {
insertFolders(folders) insertFolders(folders)
insertFeeds(feverFeeds) val newFeeds = insertFeeds(feverFeeds)
insertItems(items) val newItems = insertItems(items)
insertItemsIds(unreadIds, starredIds.toMutableList()) insertItemsIds(unreadIds, starredIds.toMutableList())
// We store the id to use for the next synchronisation even if it's not a timestamp // We store the id to use for the next synchronisation even if it's not a timestamp
database.accountDao().updateLastModified(sinceId, account.id) database.accountDao().updateLastModified(sinceId, account.id)
SyncResult() SyncResult(
items = newItems,
feeds = newFeeds
)
} }
} }
@ -144,7 +148,7 @@ class FeverRepository(
database.folderDao().upsertFolders(folders, account) database.folderDao().upsertFolders(folders, account)
} }
private suspend fun insertFeeds(feverFeeds: FeverFeeds) = with(feverFeeds) { private suspend fun insertFeeds(feverFeeds: FeverFeeds): List<Feed> = with(feverFeeds) {
for (feed in feeds) { for (feed in feeds) {
for ((folderId, feedsIds) in feedsGroups) { for ((folderId, feedsIds) in feedsGroups) {
if (feedsIds.contains(feed.remoteId!!.toInt())) { if (feedsIds.contains(feed.remoteId!!.toInt())) {
@ -154,23 +158,23 @@ class FeverRepository(
} }
feeds.forEach { it.accountId = account.id } feeds.forEach { it.accountId = account.id }
database.feedDao().upsertFeeds(feeds, account) return database.feedDao().upsertFeeds(feeds, account)
} }
private suspend fun insertItems(items: List<Item>): List<Item> { private suspend fun insertItems(items: List<Item>): List<Item> {
val newItems = arrayListOf<Item>() val newItems = arrayListOf<Item>()
val itemsFeedsIds = mutableMapOf<String, Int>() val itemsFeedsIds = mutableMapOf<String?, Int>()
for (item in items) { for (item in items) {
var feedId: Int? val feedId: Int
if (itemsFeedsIds.containsKey(item.feedRemoteId)) { if (itemsFeedsIds.containsKey(item.feedRemoteId)) {
feedId = itemsFeedsIds[item.feedRemoteId] feedId = itemsFeedsIds.getValue(item.feedRemoteId)
} else { } else {
//feedId = database.feedDao().getFeedIdByRemoteId(item.feedRemoteId!!, account.id) feedId = database.feedDao().selectRemoteFeedLocalId(item.feedRemoteId!!, account.id)
// itemsFeedsIds[item.feedRemoteId!!] = feedId itemsFeedsIds[item.feedRemoteId!!] = feedId
} }
//item.feedId = feedId!! item.feedId = feedId
item.text?.let { item.readTime = Utils.readTimeFromString(it) } item.text?.let { item.readTime = Utils.readTimeFromString(it) }
newItems += item newItems += item