SHORT BREAK

💣 Fuck-up night tales over a beer 🍻

Vladislav Ertel

Amsterdam, 2025

Who am I?

Today we are going to talk about:

  • One of 1001 wrong ways to download a file
  • What is the most unobvious place for test flakiness to occur on CI?
  • What should you keep in mind when developing a public service?

Chapter #1: How to DDOS yourself

Code With Me

"Fairly simple"

"Fairly simple"

One more minute of boring stuff

the Fix

the proper fix


						val isSuccess = makeRequest()
						if (!isSuccess) {
							retry(2.seconds)
						}
					

the proper fix


						val isSuccess = makeRequest()
						if (!isSuccess) {
							val expPower = attemp + small_random
							val delayTime = exp(2, expPower) + bigger_random
							retry(delayTime.seconds)
						}
					

Takeaway #1

1. Scale up your infrastructure in advance

2. Make your retries exponential

3. Ask SRE team for an advice

Chapter #2: It's all about files

Time to get serious

1. Download an IDE on a remote host

2. Download an IDE locally

3. Connect them together

Quick recap of Gateway

Quick recap of Gateway

Quick recap of Gateway

Quick recap of Gateway

Quick recap of Gateway

Quick recap of Gateway

Quick recap of Gateway

Where the data is coming from?

https://data.services.jetbrains.com/products?code=QA


[{"code":"QA","intellijProductCode":"QA","alternativeCodes":["QA"],"salesCode":"QA","name":"Aqua",
"productFamilyName":"Aqua","link":"https://www.jetbrains.com/aqua/","description":"A powerful IDE for
test automation","tags":[{"id":"java","name":"Java"},{"id":"kotlin","name":"Kotlin"},{"id":"python","
name":"Python"},{"id":"js","name":"JavaScript, TypeScript"},{"id":"sql","name":"SQL/NoSQL"}],"types":[],"
categories":["IDE"],"releases":[{"date":"2025-01-24","type":"release","downloads":{"linuxARM64":{"link":"htt
ps://download.jetbrains.com/aqua/aqua-2024.3.2-aarch64.tar.gz","size":1077325995,"checksumLink":"https://do
wnload.jetbrains.com/aqua/aqua-2024.3.2-aarch64.tar.gz.sha256"},"linux":{"link":"https://download.jetbrai
ns.com/aqua/aqua-2024.3.2.tar.gz","size":1077154153,"checksumLink":"https://download.jetbrains.com/aqua/aqua-2
024.3.2.tar.gz.sha256"},"thirdPartyLibrariesJson":{"link":"https://resources.jetbrains.com/storage/third-party-
libraries/aqua/aqua-2024.3.2-third-party-libraries.json","size":68408,"checksumLink":"https://resources.jetbrain
s.com/storage/third-party-libraries/aqua/aqua-2024.3.2-third-party-libraries.json.sha256"},"windows":{"link":"ht
tps://download.jetbrains.com/aqua/aqua-2024.3.2.exe","size":778054112,"checksumLink":"https://download.jetbrains
.com/aqua/aqua-2024.3.2.exe.sha256"},"windowsARM64":{"link":"https://download.jetbrains.com/aqua/aqua-2024.3.2-aa
rch64.exe","size":749261640,"checksumLink":"https://download.jetbrains.com/aqua/aqua-2024.3.2-aarch64.exe.sha256
"},"macM1":{"link":"https://download.jetbrains.com/aqua/aqua-2024.3.2-aarch64.dmg","size":1029961397,"checksumLi
nk":"https://download.jetbrains.com/aqua/aqua-2024.3.2-aarch64.dmg.sha256"},"mac":{"link":"https://download.jetb
rains.com/aqua/aqua-2024.3.2.dmg","size":1039127577,"checksumLink":"https://download.jetbrains.com/aqua/aqua-
2024.3.2.dmg.sha256"}},"patches":{},"notesLink":null,"licenseRequired":true,"version":"2024.3.2","majorVersi
on":"2024.3","build":"243.23654.154","whatsnew":"Aqua 2024.3.2 is out. Whats' new:\n Minor
fixes and stability improvements","uninstallFeedbackLinks":null,"printableReleaseType":null},{"d
ate":"2024-12-20","type":"release","downloads":{"linuxARM64":{"link":"https://download.jetbrains.com/aqua/aqua
					

						@Serializable
						data class Download(
							val link: String,
							val size: Int? = null,
							val checksumLink: String? = null,
						)
					

Do you see the problem?

2048MB ought to be enough for anybody
Vladislav Ertel, 2021

Fix


						@Serializable
						data class Download(
							val link: String,
							val size: Int? = null,
							val checksumLink: String? = null,
						)
					

Fix


						@Serializable
						data class Download(
							val link: String,
							val size: Long? = null,
							val checksumLink: String? = null,
						)
					

Takeaway #2

1. It's 2025. Don't use Int when working with files, use Long

2. If you have gone past 2 GB: prepare for 4 GB

Chapter #3: STDIO

It works but there is a nuance

Boring slides once again

$PATH, $http_proxy, keychain, ulimit

$ ssh my.lovely.host

~/.profile, ~/.bash_profile, ~/.zprofile, ~/.bashrc, ~/.zshrc, ....


						/**
						* People of the future, I'm really sorry for this regex,
						* but I needed somehow to pass all information
						* about deploy via log message :(
						*/
						private const val SuccessfulDeployDelimiter = "#\$hi#"

						private const val SuccessfulDeployPattern = "$SuccessfulDeployPrefix\\. " +
							"idePath=#\\\$hi#(.*)#\\\$hi# " +
							"productCode=#\\\$hi#($productCodePattern)#\\\$hi# " +
							...
					

						@Serializable
						private data class SuccessfulDeployInfo (
							val idePath: String,
							val productCode: String,
							....)
						...
						internal const val SuccessfulDeployPrefix = "Deployment Successful #\$hi#"
						private const val messageSuffix = "#\$poka#"
						...
						val jsonStr = message.substringAfter(SuccessfulDeployPrefix)
											.substringBefore(messageSuffix)
						val deployInfo = try {
						  SuccessfulDeployInfo.fromJson(jsonStr)
						} catch (e: SerializationException) { ... }
					

ssh my.host ./go-worker --trace do_stuff


						val returnCode = execute(command)
						val stdout = command.readStdout()
					

						remote program                        Gateway code
						      ⬇️                                     ⬆️
						      ⬇️                                     ⬆️
						sshd (server) ->       TCP      ->          sshj
						                                   (client library)
					

write(stdout, log_string) // <- blocked the thread

Takeaway #3

Be careful when using stdio in complex situations:

1. There is no structure in the output payload

2. You have to read from the stdio pipe of a process

3. Don't use login shell if you need to parse output of a command

Chapter #4: Chained by a build chain

jetbrains://fix/all/bugs

/installIde

/listIde

/login

...

/listHandledUrls

/installIde

/listIde

/login

...

/listHandledUrls - 404: Not found

Artifact dependencies!

use

Teamcity dependencies

Artifact vs Snapshot dependencies
  • Snapshot = run build with the same sources (vcs revision)*checkout rules apply
  • Artifact = give me an artifact
  • Snapshot + Artifact = give me an artifact with the same source revision*

/installIde

/listIde

/login

...

/listHandledUrls - 404: Not found

use

How it was


							dependencies {
								dependency(installerBuild) {
									artifacts {
										buildRule = lastSuccessful()
										artifactRules = artifactRule
									}
								}
							}
					

How it had to be


						dependencies {
							dependency(installerBuild) {
								snapshot {
									synchronizeRevisions = true
								}
								artifacts {
									artifactRules = artifactRule
								}
							}
						}
					

Takeaway #4

Be careful when using artifact-only dependencies

Be careful when using artifact-only dependencies Teamcity

One last takeaway

Don't be afraid to ask expirienced colleagus

@Vlada

@Vladislav.Ertel

We are hiring

The end

SHORT BREAK

Settings Drift: A Tale of Broken Import and IDE Amnesia

  • Third-party issue?
  • Third-party issue?
  • Third-party issue?

Settings Migration

  • ~/…/Rider2023.3
  • ↓↓↓↓↓
  • ~/…/Rider2024.1
🎉 ✓ CORRECT ✓ 🎊

The End

[ending 1/2]

Settings Migration

(the silent update)

ConfigImportHelper.java: 1819 lines

Settings Migration

  1. Toolbox-performed update
  2. Custom migration command support
  3. Per-product customizations (AppCode, CLion, JetBrainsClient, Rider)
  4. Special migrations for plugins
  5. Many more

IDE Update Via Toolbox

  1. Download the IDE archive (problems)
  2. Unpack the archive (problems)

    (upvote TBX-6708)
  3. Run the IDE with "update" arg (problems)
    (generate the actual update script)
  4. Run the IDE with "update" arg (problems)
    (perform the plugin update)
  5. Finish (problems)

IDE Update Via Toolbox

  1. Download the IDE archive (problems)
  2. Unpack the archive (problems)

    (upvote TBX-6708)
  3. Run the IDE with "update" arg (problems)
    (generate the actual update script)
  4. Run the IDE with "update" arg (problems)
    (perform the plugin update)
  5. Finish (problems)

The Source

IDE will only perform the silent import into the unused settings folder.
Non-empty settings folder is not replaced.

IDE Update [1/2]

  1. Run the IDE with "update" arg
(ruined update)

IDE Update [1/2]

  1. Run the IDE with "update" arg
(backend not triggered)

IDE Update [1/2]

  1. Run the IDE with "update" arg
(ruined update with EBS)

IDE Update [1/2]

  1. Run the IDE with "update" arg
(ruined update with EBS)

IDE Update [1/2]

  1. Run the IDE with "update" arg
    • Problem: ReSharper EBS process
    • Problem: ReSharper settings import
      (only if ReSharper is used)

IDE Update [2/2]

  1. Finish (copy the settings during the next run)
    • Alive processes keeping the settings dir from copying (upvote IJPL-148314)

IDE Update [3/2]

  1. The Unknown
    • We do not know.

Investigation

  1. Rider: Must be a Toolbox problem
  2. Toolbox: Must be a Rider problem
  3. Rider: Must be a Toolbox problem?
  4. …⚽…
  5. IJPL-159952: Import diagnostics

Investigation

Collect logs from both sources:
  • The IDE
  • The Toolbox
--- IDE STARTED ---
#c.i.p.i.b.AppStarter - Will skip the config import to directory
"C:\Users\fried\AppData\Roaming\JetBrains\IntelliJIdea2025.1"
(exists = true). Current entries: "app-internal-state.db",
"bundled_plugins.txt", "c.kdbx", "c.pwd", …

Conclusion

  1. When having a problem with import, remember that we have logs (both IDE and Toolbox).
  2. Coordinate investigation of important issues.
  3. Do not play ⚽soccer, play 🏈football.

The End

[ending 2/2]