Skip to content

Fallback try catch block #6873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
import org.jetbrains.annotations.VisibleForTesting

/** Repository to persist session data to be shared between all app processes. */
internal interface SharedSessionRepository {
Expand All @@ -48,20 +50,38 @@ constructor(
@Background private val backgroundDispatcher: CoroutineContext,
) : SharedSessionRepository {
/** Local copy of the session data. Can get out of sync, must be double-checked in datastore. */
private lateinit var localSessionData: SessionData
@VisibleForTesting lateinit var localSessionData: SessionData

/**
* Either notify the subscribers with general multi-process supported session or fallback local
* session
*/
private enum class NotificationType {
GENERAL,
FALLBACK
}

init {
println("session repo init")
CoroutineScope(backgroundDispatcher).launch {
sessionDataStore.data.collect { sessionData ->
localSessionData = sessionData
val sessionId = sessionData.sessionDetails.sessionId

FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
// Notify subscribers, regardless of sampling and data collection state
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(sessionId))
Log.d(TAG, "Notified ${subscriber.sessionSubscriberName} of new session $sessionId")
sessionDataStore.data
.catch {
val newSession =
SessionData(
sessionDetails = sessionGenerator.generateNewSession(null),
backgroundTime = timeProvider.currentTime()
)
Log.d(
TAG,
"Init session datastore failed with exception message: ${it.message}. Emit fallback session ${newSession.sessionDetails.sessionId}"
)
emit(newSession)
}
.collect { sessionData ->
localSessionData = sessionData
val sessionId = sessionData.sessionDetails.sessionId
notifySubscribers(sessionId, NotificationType.GENERAL)
}
}
}
}

Expand All @@ -74,9 +94,14 @@ constructor(
Log.d(TAG, "App backgrounded on ${getProcessName()} - $sessionData")

CoroutineScope(backgroundDispatcher).launch {
sessionDataStore.updateData {
// TODO(mrober): Double check time makes sense?
sessionData.copy(backgroundTime = timeProvider.currentTime())
try {
sessionDataStore.updateData {
// TODO(mrober): Double check time makes sense?
sessionData.copy(backgroundTime = timeProvider.currentTime())
}
} catch (ex: Exception) {
Log.d(TAG, "App backgrounded, failed to update data. Message: ${ex.message}")
localSessionData = localSessionData.copy(backgroundTime = timeProvider.currentTime())
}
}
}
Expand All @@ -91,20 +116,47 @@ constructor(

if (shouldInitiateNewSession(sessionData)) {
CoroutineScope(backgroundDispatcher).launch {
sessionDataStore.updateData { currentSessionData ->
// Double-check pattern
if (shouldInitiateNewSession(currentSessionData)) {
val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
currentSessionData.copy(sessionDetails = newSessionDetails)
} else {
currentSessionData
try {
sessionDataStore.updateData { currentSessionData ->
// Double-check pattern
if (shouldInitiateNewSession(currentSessionData)) {
val newSessionDetails =
sessionGenerator.generateNewSession(sessionData.sessionDetails)
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
currentSessionData.copy(sessionDetails = newSessionDetails)
} else {
currentSessionData
}
}
} catch (ex: Exception) {
Log.d(TAG, "App appForegrounded, failed to update data. Message: ${ex.message}")
val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
localSessionData = localSessionData.copy(sessionDetails = newSessionDetails)
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)

val sessionId = newSessionDetails.sessionId
notifySubscribers(sessionId, NotificationType.FALLBACK)
}
}
}
}

private suspend fun notifySubscribers(sessionId: String, type: NotificationType) {
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
// Notify subscribers, regardless of sampling and data collection state
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(sessionId))
when (type) {
NotificationType.GENERAL ->
Log.d(TAG, "Notified ${subscriber.sessionSubscriberName} of new session $sessionId")
NotificationType.FALLBACK ->
Log.d(
TAG,
"Notified ${subscriber.sessionSubscriberName} of new fallback session $sessionId"
)
}
}
}

private fun shouldInitiateNewSession(sessionData: SessionData): Boolean {
val interval = timeProvider.currentTime() - sessionData.backgroundTime
return interval > sessionsSettings.sessionRestartTimeout
Expand Down
Loading