Skip to content
Open
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions ee/backend/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ dependencies {
implementation libs.springDocOpenApiCommon


/**
* OpenTelemetry for distributed tracing
*/
implementation libs.opentelemetryApi
implementation libs.opentelemetryInstrumentationAnnotations

/**
* MISC
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package io.tolgee.ee.service.branching

import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.annotations.WithSpan
import io.tolgee.model.branching.Branch
import io.tolgee.repository.KeyRepository
import io.tolgee.service.branching.BranchCopyService
import io.tolgee.tracing.TolgeeTracingContext
import io.tolgee.util.Logging
import jakarta.persistence.EntityManager
import jakarta.transaction.Transactional
Expand All @@ -12,6 +15,7 @@ import org.springframework.stereotype.Service
class BranchCopyServiceSql(
private val entityManager: EntityManager,
private val keyRepository: KeyRepository,
private val tracingContext: TolgeeTracingContext,
) : BranchCopyService,
Logging {
companion object {
Expand All @@ -33,6 +37,7 @@ class BranchCopyServiceSql(
*
* BATCHING: Processes keys in batches of 1000 to avoid SQL timeout on large projects.
*/
@WithSpan
@Transactional
override fun copy(
projectId: Long,
Expand All @@ -41,14 +46,15 @@ class BranchCopyServiceSql(
) {
require(sourceBranch.id != targetBranch.id) { "Source and target branch must differ" }

// Get total key count for batching
val totalKeys =
keyRepository.countByProjectAndBranchIncludingOrphan(
projectId,
sourceBranch.id,
sourceBranch.isDefault,
)

setTracingAttributes(projectId, sourceBranch, targetBranch, totalKeys)

// Create empty temporary key mapping table once (reused across batches)
traceLogMeasureTime("branchCopyService: createKeyMappingTable") {
createKeyMappingTable()
Expand Down Expand Up @@ -104,10 +110,28 @@ class BranchCopyServiceSql(
}
}

private fun setTracingAttributes(
projectId: Long,
sourceBranch: Branch,
targetBranch: Branch,
totalKeys: Long,
) {
tracingContext.setContext(projectId, null)

val span = Span.current()
span.setAttribute("tolgee.branch.source.id", sourceBranch.id)
span.setAttribute("tolgee.branch.source.name", sourceBranch.name)
span.setAttribute("tolgee.branch.target.id", targetBranch.id)
span.setAttribute("tolgee.branch.target.name", targetBranch.name)
span.setAttribute("tolgee.branch.copy.totalKeys", totalKeys)
span.setAttribute("tolgee.branch.copy.batchSize", BATCH_SIZE.toLong())
}

/**
* Creates an empty temporary table for key mapping.
* This table is created once and reused across all batches.
*/
@WithSpan
private fun createKeyMappingTable() {
val createTableSql = """
CREATE TEMPORARY TABLE temp_key_mapping (
Expand All @@ -126,6 +150,7 @@ class BranchCopyServiceSql(
* Truncates and populates the key mapping table for a batch.
* Maps source key IDs to target key IDs.
*/
@WithSpan
private fun createKeyMapping(
projectId: Long,
sourceBranch: Branch,
Expand Down Expand Up @@ -177,6 +202,7 @@ class BranchCopyServiceSql(
/**
* Copies key metas using the pre-computed key mapping.
*/
@WithSpan
private fun copyKeyMetas(targetBranch: Branch) {
val sql = """
INSERT INTO key_meta (id, key_id, description, custom, created_at, updated_at)
Expand All @@ -194,6 +220,7 @@ class BranchCopyServiceSql(
/**
* Copies key meta tags using the pre-computed key mapping.
*/
@WithSpan
private fun copyKeyMetaTags() {
val sql = """
INSERT INTO key_meta_tags (key_metas_id, tags_id)
Expand All @@ -209,6 +236,7 @@ class BranchCopyServiceSql(
/**
* Copies key meta comments using the pre-computed key mapping.
*/
@WithSpan
private fun copyKeyMetaComments(targetBranch: Branch) {
val sql = """
INSERT INTO key_comment (id, key_meta_id, author_id, text, from_import, created_at, updated_at)
Expand All @@ -228,6 +256,7 @@ class BranchCopyServiceSql(
/**
* Copies key meta code references using the pre-computed key mapping.
*/
@WithSpan
private fun copyKeyMetaCodeReferences(targetBranch: Branch) {
val sql = """
INSERT INTO key_code_reference (id, key_meta_id, author_id, path, line, from_import, created_at, updated_at)
Expand All @@ -247,6 +276,7 @@ class BranchCopyServiceSql(
/**
* Copies key screenshot references using the pre-computed key mapping.
*/
@WithSpan
private fun copyKeyScreenshotReferences() {
val sql = """
INSERT INTO key_screenshot_reference (key_id, screenshot_id, positions, original_text)
Expand All @@ -260,6 +290,7 @@ class BranchCopyServiceSql(
/**
* Copies translation comments using the pre-computed key mapping.
*/
@WithSpan
private fun copyTranslationComments(targetBranch: Branch) {
val sql = """
INSERT INTO translation_comment (id, text, state, translation_id, author_id, created_at, updated_at)
Expand All @@ -277,6 +308,7 @@ class BranchCopyServiceSql(
.executeUpdate()
}

@WithSpan
private fun copyKeys(
projectId: Long,
sourceBranch: Branch,
Expand Down Expand Up @@ -327,6 +359,7 @@ class BranchCopyServiceSql(
* Copies translations using the pre-computed key mapping.
* Much simpler and faster than the original 4-way join.
*/
@WithSpan
private fun copyTranslations(targetBranch: Branch) {
val sql = """
INSERT INTO translation (
Expand All @@ -349,6 +382,7 @@ class BranchCopyServiceSql(
/**
* Copies translation labels using the pre-computed key mapping.
*/
@WithSpan
private fun copyTranslationLabels() {
val sql = """
INSERT INTO translation_label (translation_id, label_id)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.tolgee.ee.service.branching

import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.annotations.WithSpan
import io.tolgee.Metrics
import io.tolgee.constants.Message
import io.tolgee.dtos.queryResults.branching.BranchMergeChangeView
Expand All @@ -24,6 +26,7 @@ import io.tolgee.model.translation.Translation
import io.tolgee.repository.KeyRepository
import io.tolgee.service.branching.AbstractBranchMergeService
import io.tolgee.service.language.LanguageService
import io.tolgee.tracing.TolgeeTracingContext
import io.tolgee.util.Logging
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Primary
Expand All @@ -45,6 +48,7 @@ class BranchMergeService(
@Lazy
private val languageService: LanguageService,
private val metrics: Metrics,
private val tracingContext: TolgeeTracingContext,
) : AbstractBranchMergeService(branchMergeChangeRepository),
Logging {
@Transactional
Expand All @@ -56,11 +60,14 @@ class BranchMergeService(
branchMerge.changes.addAll(changes)
}

@WithSpan
@Transactional
fun dryRun(
sourceBranch: Branch,
targetBranch: Branch,
): BranchMerge {
setDryRunTracingAttributes(sourceBranch, targetBranch)

return metrics.branchMergePreviewTimer.recordCallable {
val branchMerge =
BranchMerge().apply {
Expand All @@ -71,6 +78,11 @@ class BranchMergeService(
}
dryRun(branchMerge)
branchMergeRepository.save(branchMerge)

val span = Span.current()
span.setAttribute("tolgee.branch.merge.id", branchMerge.id)
span.setAttribute("tolgee.branch.merge.changesCount", branchMerge.changes.size.toLong())

branchMerge
}!!
}
Expand Down Expand Up @@ -98,7 +110,35 @@ class BranchMergeService(
val targetKeyId: Long?,
)

private fun setDryRunTracingAttributes(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might sound silly, but I really don't like having all the tracing code directly inside the services, it makes them feel bloated and harder to read .. I’d personally prefer moving this into a separate layer/service if possible .. wdyt ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i also don't like it, so at least moved it to separate methods. And asked Gabe for the same thing in his pr. We still don't have it in many places so it can be a good time to move it.
However, seems like the attributes of span are very context specific, so i'm not sure right now how to make it more universal. Probably there is a way

sourceBranch: Branch,
targetBranch: Branch,
) {
tracingContext.setContext(sourceBranch.project.id, null)

val span = Span.current()
span.setAttribute("tolgee.branch.source.id", sourceBranch.id)
span.setAttribute("tolgee.branch.source.name", sourceBranch.name)
span.setAttribute("tolgee.branch.target.id", targetBranch.id)
span.setAttribute("tolgee.branch.target.name", targetBranch.name)
}

private fun setApplyMergeTracingAttributes(merge: BranchMerge) {
tracingContext.setContext(merge.sourceBranch.project.id, null)

val span = Span.current()
span.setAttribute("tolgee.branch.merge.id", merge.id)
span.setAttribute("tolgee.branch.source.id", merge.sourceBranch.id)
span.setAttribute("tolgee.branch.source.name", merge.sourceBranch.name)
span.setAttribute("tolgee.branch.target.id", merge.targetBranch.id)
span.setAttribute("tolgee.branch.target.name", merge.targetBranch.name)
span.setAttribute("tolgee.branch.merge.changesCount", merge.changes.size.toLong())
}

@WithSpan
fun applyMerge(merge: BranchMerge) {
setApplyMergeTracingAttributes(merge)

metrics.branchMergeApplyTimer.record(
Runnable {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.tolgee.ee.service.branching

import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.annotations.WithSpan
import io.tolgee.Metrics
import io.tolgee.activity.ActivityHolder
import io.tolgee.activity.data.RevisionType
Expand All @@ -23,6 +25,7 @@ import io.tolgee.model.enums.BranchKeyMergeChangeType
import io.tolgee.security.authentication.AuthenticationFacade
import io.tolgee.service.branching.AbstractBranchService
import io.tolgee.service.branching.BranchCopyService
import io.tolgee.tracing.TolgeeTracingContext
import jakarta.persistence.EntityManager
import org.springframework.context.annotation.Primary
import org.springframework.data.domain.Page
Expand All @@ -44,6 +47,7 @@ class BranchServiceImpl(
private val activityHolder: ActivityHolder,
private val branchCleanupService: BranchCleanupService,
private val metrics: Metrics,
private val tracingContext: TolgeeTracingContext,
) : AbstractBranchService(branchRepository, branchMergeService) {
override fun getBranches(
projectId: Long,
Expand Down Expand Up @@ -104,6 +108,7 @@ class BranchServiceImpl(
?: throw NotFoundException(Message.BRANCH_NOT_FOUND)
}

@WithSpan
@Transactional
override fun createBranch(
projectId: Long,
Expand All @@ -117,13 +122,17 @@ class BranchServiceImpl(
Message.ORIGIN_BRANCH_NOT_FOUND,
)

setCreateBranchTracingAttributes(projectId, name, originBranch)

val branch =
createBranch(projectId, name, author).also {
it.originBranch = originBranch
it.pending = true
}
branchRepository.save(branch)

Span.current().setAttribute("tolgee.branch.id", branch.id)

branchCopyService.copy(projectId, originBranch, branch)
branchSnapshotService.createInitialSnapshot(projectId, originBranch, branch)
branch
Expand All @@ -147,6 +156,36 @@ class BranchServiceImpl(
}
}

private fun setCreateBranchTracingAttributes(
projectId: Long,
name: String,
originBranch: Branch,
) {
tracingContext.setContext(projectId, null)

val span = Span.current()
span.setAttribute("tolgee.branch.name", name)
span.setAttribute("tolgee.branch.origin.id", originBranch.id)
span.setAttribute("tolgee.branch.origin.name", originBranch.name)
}

private fun setApplyMergeTracingAttributes(
projectId: Long,
mergeId: Long,
deleteBranch: Boolean?,
merge: BranchMerge,
) {
tracingContext.setContext(projectId, null)

val span = Span.current()
span.setAttribute("tolgee.branch.merge.id", mergeId)
span.setAttribute("tolgee.branch.merge.deleteBranch", deleteBranch ?: true)
span.setAttribute("tolgee.branch.source.id", merge.sourceBranch.id)
span.setAttribute("tolgee.branch.source.name", merge.sourceBranch.name)
span.setAttribute("tolgee.branch.target.id", merge.targetBranch.id)
span.setAttribute("tolgee.branch.target.name", merge.targetBranch.name)
}

@Transactional
override fun deleteBranch(
projectId: Long,
Expand Down Expand Up @@ -184,6 +223,7 @@ class BranchServiceImpl(
return branchMergeService.dryRun(sourceBranch, targetBranch)
}

@WithSpan
@Transactional
override fun applyMerge(
projectId: Long,
Expand All @@ -193,6 +233,9 @@ class BranchServiceImpl(
val merge =
branchMergeService.findActiveMergeFull(projectId, mergeId)
?: throw NotFoundException(Message.BRANCH_MERGE_NOT_FOUND)

setApplyMergeTracingAttributes(projectId, mergeId, deleteBranch, merge)

if (!merge.isReadyToMerge) {
if (!merge.isRevisionValid) {
throw BadRequestException(Message.BRANCH_MERGE_REVISION_NOT_VALID)
Expand Down
Loading