Skip to content

Commit

Permalink
Merge pull request #884 from embrace-io/hho/failed-span-endtime
Browse files Browse the repository at this point in the history
Add fallback end time for spans in a terminated session
  • Loading branch information
bidetofevil committed May 24, 2024
2 parents be623ba + 7ca1981 commit 6e92cf7
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ internal class EmbraceDeliveryService(
val completedSpanIds = sessionMessage.spans?.map { it.spanId }?.toSet() ?: emptySet()
val spansToFail = sessionMessage.spanSnapshots
?.filterNot { completedSpanIds.contains(it.spanId) }
?.map { it.toFailedSpan(sessionMessage.session.endTime ?: 0L) }
?.map { it.toFailedSpan(endTimeMs = getFailedSpanEndTimeMs(sessionMessage)) }
?: emptyList()
val completedSpans = (sessionMessage.spans ?: emptyList()) + spansToFail
sessionMessage.copy(spans = completedSpans, spanSnapshots = emptyList())
Expand Down Expand Up @@ -175,6 +175,12 @@ internal class EmbraceDeliveryService(
}
}

private fun getFailedSpanEndTimeMs(sessionMessage: SessionMessage) =
sessionMessage.session.endTime
?: sessionMessage.session.terminationTime
?: sessionMessage.session.lastHeartbeatTime
?: sessionMessage.session.startTime

override fun sendMoment(eventMessage: EventMessage) {
apiService.sendEvent(eventMessage)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import io.embrace.android.embracesdk.fakes.FakeSessionIdTracker
import io.embrace.android.embracesdk.fakes.FakeSpanData.Companion.perfSpanSnapshot
import io.embrace.android.embracesdk.fakes.FakeStorageService
import io.embrace.android.embracesdk.fakes.TestPlatformSerializer
import io.embrace.android.embracesdk.fakes.fakeCachedV1SessionMessageWithHeartbeatTime
import io.embrace.android.embracesdk.fakes.fakeCachedV1SessionMessageWithTerminationTime
import io.embrace.android.embracesdk.fakes.fakeSession
import io.embrace.android.embracesdk.fakes.fakeV1EndedSessionMessage
import io.embrace.android.embracesdk.fakes.fakeV1EndedSessionMessageWithSnapshot
Expand Down Expand Up @@ -147,6 +149,42 @@ internal class EmbraceDeliveryServiceTest {
)
}

@Test
fun `missing end time in session will find appropriate fallback for end time of failed span`() {
assertNotNull(cacheService.writeSession(sessionWithTerminationTimeFileName, sessionWithTerminationTime))
deliveryService.sendCachedSessions({ fakeNativeCrashService }, sessionIdTracker)
val sentSessionWithTerminationTime = apiService.sessionRequests.single()
assertNotNull(cacheService.writeSession(sessionWithLastHeartbeatTimeFileName, sessionWithLastHeartbeatTime))
deliveryService.sendCachedSessions({ fakeNativeCrashService }, sessionIdTracker)
val sentSessionWithLastHeartbeatTime = apiService.sessionRequests.last()

checkNotNull(sessionWithTerminationTime.spanSnapshots).single().let { snapshot ->
assertEmbraceSpanData(
span = sentSessionWithTerminationTime.spans?.single { it.spanId == snapshot.spanId },
expectedStartTimeMs = checkNotNull(snapshot.startTimeNanos.nanosToMillis()),
expectedEndTimeMs = checkNotNull(sessionWithTerminationTime.session.terminationTime),
expectedParentId = SpanId.getInvalid(),
expectedErrorCode = ErrorCode.FAILURE,
expectedCustomAttributes = mapOf(
EmbType.Performance.Default.toEmbraceKeyValuePair()
)
)
}

checkNotNull(sessionWithLastHeartbeatTime.spanSnapshots).single().let { snapshot ->
assertEmbraceSpanData(
span = sentSessionWithLastHeartbeatTime.spans?.single { it.spanId == snapshot.spanId },
expectedStartTimeMs = checkNotNull(snapshot.startTimeNanos.nanosToMillis()),
expectedEndTimeMs = checkNotNull(sessionWithLastHeartbeatTime.session.lastHeartbeatTime),
expectedParentId = SpanId.getInvalid(),
expectedErrorCode = ErrorCode.FAILURE,
expectedCustomAttributes = mapOf(
EmbType.Performance.Default.toEmbraceKeyValuePair()
)
)
}
}

@Test
fun `do not add failed span from a snapshot if a span with the same id is already in the payload`() {
val startedSnapshot = EmbraceSpanData(perfSpanSnapshot)
Expand Down Expand Up @@ -253,6 +291,18 @@ internal class EmbraceDeliveryServiceTest {
sessionWithSnapshot.session.startTime,
false
).filename
private val sessionWithTerminationTime = fakeCachedV1SessionMessageWithTerminationTime()
private val sessionWithTerminationTimeFileName = CachedSession.create(
sessionWithTerminationTime.session.sessionId,
sessionWithTerminationTime.session.startTime,
false
).filename
private val sessionWithLastHeartbeatTime = fakeCachedV1SessionMessageWithHeartbeatTime()
private val sessionWithLastHeartbeatTimeFileName = CachedSession.create(
sessionWithLastHeartbeatTime.session.sessionId,
sessionWithLastHeartbeatTime.session.startTime,
false
).filename
private val logsEnvelope = Envelope(
resource = FakeEnvelopeResourceSource().resource,
metadata = FakeEnvelopeMetadataSource().metadata,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ internal fun fakeV1EndedSessionMessageWithSnapshot(): SessionMessage = SessionMe
spanSnapshots = listOfNotNull(FakePersistableEmbraceSpan.started().snapshot()?.toOldPayload()),
)

internal fun fakeCachedV1SessionMessageWithTerminationTime(): SessionMessage = SessionMessage(
session = fakeSession().copy(
sessionId = "fakeSessionWithTerminationTime",
startTime = 161000000000L,
terminationTime = 161000500000L
),
spans = listOfNotNull(testSpan),
spanSnapshots = listOfNotNull(FakePersistableEmbraceSpan.started().snapshot()?.toOldPayload()),
)

internal fun fakeCachedV1SessionMessageWithHeartbeatTime(): SessionMessage = SessionMessage(
session = fakeSession().copy(
sessionId = "fakeSessionWithHeartbeat",
startTime = 161000000000L,
lastHeartbeatTime = 161000600000L
),
spans = listOfNotNull(testSpan),
spanSnapshots = listOfNotNull(FakePersistableEmbraceSpan.started().snapshot()?.toOldPayload()),
)

internal fun fakeV2SessionMessage(): SessionMessage = SessionMessage(
session = fakeSession(),
metadata = EnvelopeMetadata(),
Expand Down

0 comments on commit 6e92cf7

Please sign in to comment.