LAB-29: record id to group candidates and opened file

GitOrigin-RevId: df6323afd3370c0a804202e7fd35986154e83960
This commit is contained in:
Svetlana.Zemlyanskaya
2020-05-07 12:37:09 +00:00
committed by intellij-monorepo-bot
parent d7fef2f225
commit fd15f37929
7 changed files with 80 additions and 24 deletions
@@ -23,6 +23,7 @@
<filePrediction.featureProvider implementation="com.intellij.filePrediction.FilePredictionFileTypeFeatures"/>
<filePrediction.featureProvider implementation="com.intellij.filePrediction.history.FilePredictionHistoryFeatures"/>
<applicationService serviceImplementation="com.intellij.filePrediction.FilePredictionHandler"/>
<projectService serviceImplementation="com.intellij.filePrediction.history.FilePredictionHistory"/>
</extensions>
@@ -9,12 +9,16 @@ internal object FileNavigationLogger {
fun logEvent(project: Project,
event: String,
sessionId: Int,
features: FileFeaturesComputationResult,
filePath: String,
prevFilePath: String?,
refsComputation: Long,
probability: Double? = null) {
val data = FeatureUsageData().
addData("session_id", sessionId).
addAnonymizedPath(filePath).
addAnonymizedValue("prev_file_path", prevFilePath).
addData("refs_computation", refsComputation).
addData("features_computation", features.duration)
@@ -10,7 +10,7 @@ internal class FilePredictionEditorManagerListener : FileEditorManagerListener {
override fun selectionChanged(event: FileEditorManagerEvent) {
val newFile = event.newFile ?: return
if (ApplicationManager.getApplication().isEAP && Registry.get("filePrediction.calculate.features").asBoolean()) {
FilePredictionHandler.onFileOpened(event.manager.project, newFile, event.oldFile)
FilePredictionHandler.getInstance()?.onFileOpened(event.manager.project, newFile, event.oldFile)
}
}
}
@@ -2,18 +2,25 @@ package com.intellij.filePrediction
import com.intellij.filePrediction.history.FilePredictionHistory
import com.intellij.filePrediction.predictor.FileUsagePredictor
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.progress.util.BackgroundTaskUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.impl.ProjectManagerImpl
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.concurrency.NonUrgentExecutor
object FilePredictionHandler {
private const val CALCULATE_OPEN_FILE_PROBABILITY: Double = 0.5
class FilePredictionHandler {
companion object {
private const val CALCULATE_OPEN_FILE_PROBABILITY: Double = 0.5
private const val CALCULATE_CANDIDATE_PROBABILITY: Double = 0.1
fun getInstance(): FilePredictionHandler? = ServiceManager.getService(FilePredictionHandler::class.java)
}
private const val CALCULATE_CANDIDATE_PROBABILITY: Double = 0.1
private val predictor: FileUsagePredictor = FileUsagePredictor(30, 5, 10)
private var session: FilePredictionSessionHolder = FilePredictionSessionHolder()
fun onFileOpened(project: Project, newFile: VirtualFile, prevFile: VirtualFile?) {
if (ProjectManagerImpl.isLight(project)) {
return
@@ -21,21 +28,26 @@ object FilePredictionHandler {
NonUrgentExecutor.getInstance().execute {
BackgroundTaskUtil.runUnderDisposeAwareIndicator(project, Runnable {
if (Math.random() < CALCULATE_OPEN_FILE_PROBABILITY) {
logOpenedFile(project, newFile, prevFile)
val previousSession = session.getSession()
if (previousSession != null && previousSession.shouldLog(CALCULATE_OPEN_FILE_PROBABILITY)) {
logOpenedFile(project, previousSession.id, newFile, prevFile)
}
if (Math.random() < CALCULATE_CANDIDATE_PROBABILITY) {
predictor.predictNextFile(project, newFile)
val newSession = session.newSession()
if (newSession != null && newSession.shouldLog(CALCULATE_CANDIDATE_PROBABILITY)) {
predictor.predictNextFile(project, newSession.id, newFile)
}
FilePredictionHistory.getInstance(project).onFileOpened(newFile.url)
})
}
}
private fun logOpenedFile(project: Project, newFile: VirtualFile, prevFile: VirtualFile?) {
private fun logOpenedFile(project: Project,
sessionId: Int,
newFile: VirtualFile,
prevFile: VirtualFile?) {
val result = FilePredictionFeaturesHelper.calculateExternalReferences(project, prevFile)
val features = FilePredictionFeaturesHelper.calculateFileFeatures(project, newFile, result.value, prevFile)
FileNavigationLogger.logEvent(project, "file.opened", features, newFile.path, result.duration)
FileNavigationLogger.logEvent(project, "file.opened", sessionId, features, newFile.path, prevFile?.path, result.duration)
}
}
@@ -0,0 +1,27 @@
package com.intellij.filePrediction
import java.util.concurrent.atomic.AtomicInteger
private val counter = AtomicInteger(0)
internal class FilePredictionSessionHolder {
private var session: FilePredictionSession? = null
@Synchronized
fun getSession(): FilePredictionSession? = session
@Synchronized
fun newSession(): FilePredictionSession? {
session = FilePredictionSession()
return session
}
}
internal class FilePredictionSession {
val id = counter.incrementAndGet()
private val loggingProbability = Math.random()
fun shouldLog(threshold: Double): Boolean {
return loggingProbability < threshold
}
}
@@ -14,27 +14,28 @@ import com.intellij.openapi.vfs.VirtualFile
internal class FileUsagePredictor(private val candidatesLimit: Int,
private val logTopLimit: Int,
private val logTotalLimit: Int) {
fun predictNextFile(project: Project, file: VirtualFile) {
fun predictNextFile(project: Project, sessionId: Int, file: VirtualFile) {
val result = FilePredictionFeaturesHelper.calculateExternalReferences(project, file)
val model = getFilePredictionModel()
if (model != null) {
predictAndLogCandidates(project, model, file, result)
predictAndLogCandidates(project, sessionId, model, file, result)
}
else {
logCandidates(project, file, result)
logCandidates(project, sessionId, file, result)
}
}
private fun logCandidates(project: Project, file: VirtualFile, refs: FileReferencesComputationResult) {
private fun logCandidates(project: Project, sessionId: Int, file: VirtualFile, refs: FileReferencesComputationResult) {
val candidateFiles = provideCandidates(project, file, refs.value.references, logTotalLimit)
for (candidate in candidateFiles) {
val features = FilePredictionFeaturesHelper.calculateFileFeatures(project, candidate, refs.value, file)
FileNavigationLogger.logEvent(project, "candidate.calculated", features, candidate.path, refs.duration)
FileNavigationLogger.logEvent(project, "candidate.calculated", sessionId, features, candidate.path, file.path, refs.duration)
}
}
private fun predictAndLogCandidates(project: Project,
sessionId: Int,
model: FilePredictionModel,
file: VirtualFile,
refs: FileReferencesComputationResult) {
@@ -47,25 +48,35 @@ internal class FileUsagePredictor(private val candidatesLimit: Int,
}
candidates.sortByDescending { it.probability }
logCandidatesWithProbability(project, candidates, refs.duration)
logCandidatesWithProbability(project, sessionId, file.path, candidates, refs.duration)
}
private fun logCandidatesWithProbability(project: Project, candidates: MutableList<FilePredictionCandidate>, refsComputation: Long) {
private fun logCandidatesWithProbability(project: Project,
sessionId: Int,
prevPath: String?,
candidates: MutableList<FilePredictionCandidate>,
refsComputation: Long) {
val head = candidates.take(logTopLimit)
logCalculatedCandidates(project, head, refsComputation)
logCalculatedCandidates(project, sessionId, prevPath, head, refsComputation)
if (candidates.size > logTopLimit) {
val tail = candidates.subList(logTopLimit, candidates.size)
val randomToLog = tail.shuffled().take(logTotalLimit - logTopLimit)
logCalculatedCandidates(project, randomToLog, refsComputation)
logCalculatedCandidates(project, sessionId, prevPath, randomToLog, refsComputation)
}
}
private fun logCalculatedCandidates(project: Project, candidates: Collection<FilePredictionCandidate>, refsComputation: Long) {
private fun logCalculatedCandidates(project: Project,
sessionId: Int,
prevPath:String?,
candidates: Collection<FilePredictionCandidate>,
refsComputation: Long) {
for (candidate in candidates) {
val probability = candidate.probability
val features = candidate.features
FileNavigationLogger.logEvent(project, "candidate.calculated", features, candidate.path, refsComputation, probability)
FileNavigationLogger.logEvent(
project, "candidate.calculated", sessionId, features, candidate.path, prevPath, refsComputation, probability
)
}
}
}
@@ -29,7 +29,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
val composite = TestStatisticsEventValidatorBuilder()
.hasEventId("candidate.calculated")
.contains("probability")
.contains("probability", "session_id")
.withCustom(validator).build()
doTest(builder, predictor, expectedEvents, composite) { setPredefinedProbabilityModel(it, probabilities) }
@@ -39,7 +39,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
val predictor = FileUsagePredictor(5, 1, 3)
val validator = TestStatisticsEventValidatorBuilder()
.hasEventId("candidate.calculated")
.contains("probability").build()
.contains("probability", "session_id").build()
doTest(builder, predictor, expectedEvents, validator) { setConstantFilePredictionModel(0.1, it) }
}
@@ -48,6 +48,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
val predictor = FileUsagePredictor(5, 1, 3)
val validator = TestStatisticsEventValidatorBuilder()
.hasEventId("candidate.calculated")
.contains("session_id")
.notContains("probability").build()
doTest(builder, predictor, expectedEvents, validator) { disableFilePredictionModel() }
@@ -66,7 +67,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
modelConfigurator.invoke(testRootDisposable)
val events = collectLogEvents {
predictor.predictNextFile(myFixture.project, file!!)
predictor.predictNextFile(myFixture.project, 1, file!!)
}
val candidateEvents = events.filter { it.event.id == "candidate.calculated" }
assertEquals(expectedEvents, candidateEvents.size)