From ec3378fbbe7ca472ba55c395fcd1cec68eb73599 Mon Sep 17 00:00:00 2001 From: Olafur Geirsson Date: Wed, 4 Sep 2024 13:23:29 +0200 Subject: [PATCH] Performance: investigate memory issues We have a lot of bug reports related to high memory usage and bad performance. This PR is an attempt to identify how we can fix these problems by creating a minimized reproduction of real-world usage scenarios. See `memory.test.ts` for the reproduction. In short, we use new record/replay infrastructure to replay events from a recording of my own coding session yesterday. When replaying these events, it's clear that the "external" memory usage of the agent process quickly grows up to 2gb due to tree-sitter parsing that we run on every document content change. When we disable tree-sitter parsing, the "external" memory usage stays stable below <1mb. --- .../recording.har.yaml | 961 ++++++++++++++++++ agent/src/TestClient.ts | 2 +- agent/src/memory.test.ts | 139 ++- vscode/src/main.ts | 4 +- 4 files changed, 1071 insertions(+), 35 deletions(-) create mode 100644 agent/recordings/memory-usage_2180086764/recording.har.yaml diff --git a/agent/recordings/memory-usage_2180086764/recording.har.yaml b/agent/recordings/memory-usage_2180086764/recording.har.yaml new file mode 100644 index 000000000000..42f382d5d512 --- /dev/null +++ b/agent/recordings/memory-usage_2180086764/recording.har.yaml @@ -0,0 +1,961 @@ +log: + _recordingName: memory-usage + creator: + comment: persister:fs + name: Polly.JS + version: 6.0.6 + entries: + - _id: 8ffa79cf5668b64b3fd5ad5674e1d846 + _order: 0 + cache: {} + request: + bodySize: 0 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 288 + httpVersion: HTTP/1.1 + method: GET + queryString: [] + url: https://sourcegraph.com/.api/client-config + response: + bodySize: 188 + content: + encoding: base64 + mimeType: text/plain; charset=utf-8 + size: 188 + text: "[\"H4sIAAAAAAAAA2zMsQoCMRCE4T5PsVztE9hJsLjOznrPrBjI7koyQeW4d7dRBEn9z3xrI\ + CKaLp5eR+OlSJr2hNpl9wk3xjBwh0fXexHI+NkbXKOrsqU2NoCal47s9utXLu07aMoV\ + 0Q3yxDlb8sfQUU9S2uE0/ylhC28AAAD//wMAK/Ow9eAAAAA=\"]" + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:21 GMT + - name: content-type + value: text/plain; charset=utf-8 + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1342 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:21.412Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 9d0e130a38eaf9d3cd8d9512afc14b87 + _order: 0 + cache: {} + request: + bodySize: 144 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "144" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 319 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query ContextFilters { + site { + codyContextFilters(version: V1) { + raw + } + } + } + variables: {} + queryString: + - name: ContextFilters + value: null + url: https://sourcegraph.com/.api/graphql?ContextFilters + response: + bodySize: 104 + content: + encoding: base64 + mimeType: application/json + size: 104 + text: "[\"H4sIAAAAAAAAA6pWSkksSVSyqlYqzixJBdHJ+SmVzvl5JakVJW6ZOSWpRcUg0aLEciWrv\ + NKcnNra2loAAAAA//8DADYuyGU1AAAA\"]" + textDecoded: + data: + site: + codyContextFilters: + raw: null + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:22 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:21.815Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 460fa85fef77ffa15bb82fa3a88049a3 + _order: 0 + cache: {} + request: + bodySize: 318 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "318" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 336 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query CurrentSiteCodyLlmConfiguration { + site { + codyLLMConfiguration { + chatModel + chatModelMaxTokens + fastChatModel + fastChatModelMaxTokens + completionModel + completionModelMaxTokens + } + } + } + variables: {} + queryString: + - name: CurrentSiteCodyLlmConfiguration + value: null + url: https://sourcegraph.com/.api/graphql?CurrentSiteCodyLlmConfiguration + response: + bodySize: 270 + content: + encoding: base64 + mimeType: application/json + size: 270 + text: "[\"H4sIAAAAAAAAA3zOTQrC\",\"MBCG4bvM2tAxFcRuu7U7LzAmUxtSMyVJ/UF6d1ERxYKr\ + gQ/eh7mBpUxQ3SC5zI9rxF6326aW0LrDGCk7Cc+9o9yI5R4qoJC7KIMzhelptKxKlSQ\ + EzkqjXqHWG1h8goYuO/EcElRLjYgLaCnl+r/XkfPjiytxDT/NnDRyHHp+PPtGWxf5LN\ + GnwjIPidkrI5ajOmnVu8xqT4lhVn7RG0Scpmm6AwAA//8=\",\"AwBbliD6JQEAAA==\ + \"]" + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:20 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.441Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 7edf90ea650cb304fe26f9d57bd79477 + _order: 0 + cache: {} + request: + bodySize: 165 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "165" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 336 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query CurrentSiteCodyLlmConfiguration { + site { + codyLLMConfiguration { + smartContextWindow + } + } + } + variables: {} + queryString: + - name: CurrentSiteCodyLlmConfiguration + value: null + url: https://sourcegraph.com/.api/graphql?CurrentSiteCodyLlmConfiguration + response: + bodySize: 132 + content: + encoding: base64 + mimeType: application/json + size: 132 + text: "[\"H4sIAAAAAAAAA6pWSkksSVSyqlYqzixJBdHJ+SmVPj6+zvl5aZnppUWJJZn5eWD53MSiE\ + uf8vJLUipLwzLyU/HIlK6XUvMSknNQUpdra2loAAAAA//8DAOgINKVLAAAA\"]" + textDecoded: + data: + site: + codyLLMConfiguration: + smartContextWindow: enabled + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:20 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.479Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: d890a82b3cc2fe4d514f289e8fe6d158 + _order: 0 + cache: {} + request: + bodySize: 150 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "150" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 331 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query CurrentSiteCodyLlmProvider { + site { + codyLLMConfiguration { + provider + } + } + } + variables: {} + queryString: + - name: CurrentSiteCodyLlmProvider + value: null + url: https://sourcegraph.com/.api/graphql?CurrentSiteCodyLlmProvider + response: + bodySize: 131 + content: + encoding: base64 + mimeType: application/json + size: 131 + text: "[\"H4sIAAAAAAAAA6pWSkks\",\"SVSyqlYqzixJBdHJ+SmVPj6+zvl5aZnppUWJJZn5eSDx\ + gqL8ssyU1CIlK6Xi/NKi5NT0osSCDKXa2tpaAAAAAP//AwAfFAXARQAAAA==\"]" + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:20 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.461Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 3fec40886d87367e761715c3159e6590 + _order: 0 + cache: {} + request: + bodySize: 341 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "341" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 316 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query CurrentUser { + currentUser { + id + hasVerifiedEmail + displayName + username + avatarURL + primaryEmail { + email + } + organizations { + nodes { + id + name + } + } + } + } + variables: {} + queryString: + - name: CurrentUser + value: null + url: https://sourcegraph.com/.api/graphql?CurrentUser + response: + bodySize: 376 + content: + encoding: base64 + mimeType: application/json + size: 376 + text: "[\"H4sIAAAAAAAAA2RPy07CQBT9l7tuaQ1R2klIFAQXaOMjNBjj4nZ6aaePmToPFJr+O2kwc\ + eHunJzHvaeHHC0C64E7rUnarSE9UpEDg3SXNLxSp+T+5eqp4nPwoESTkhZ7QfmqRdEA\ + s9qRB7kwXYPHBFsCBm/KaU6Fxq5cKOvHYRiCB86QlheD+TNkysa1v5ffrQMP8IAW9fb\ + 1ERiU1naGBUFTTieFUkVDYwNX0pK0E67aAIO7ZREpvlnjV/ZOblFn1XW+Xp1+omyXRj\ + gTU5Nmm2XynM4eQnc81HMT3/gcPOi0aFEff0f0QBfw77PbYhTGazB4oHSBUpzQCiXNG\ + JMqJwPs43MYhuEMAAD//wMASoyTP04BAAA=\"]" + textDecoded: + data: + currentUser: + avatarURL: https://lh3.googleusercontent.com/a/ACg8ocKFaqbYeuBkbj5dFEzx8bXV8a7i3sVbKCNPV7G0uyvk=s96-c + displayName: SourcegraphBot-9000 + hasVerifiedEmail: true + id: VXNlcjozNDQ1Mjc= + organizations: + nodes: [] + primaryEmail: + email: sourcegraphbot9k@gmail.com + username: sourcegraphbot9k-fnwmu + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:20 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.498Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 84b962509b12000d0eef7c8a8fa655f3 + _order: 0 + cache: {} + request: + bodySize: 268 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "268" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 332 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query CurrentUserCodySubscription { + currentUser { + codySubscription { + status + plan + applyProRateLimits + currentPeriodStartAt + currentPeriodEndAt + } + } + } + variables: {} + queryString: + - name: CurrentUserCodySubscription + value: null + url: https://sourcegraph.com/.api/graphql?CurrentUserCodySubscription + response: + bodySize: 228 + content: + encoding: base64 + mimeType: application/json + size: 228 + text: "[\"H4sIAAAAAAAAA1zMsQrCMBSF4Xc5c4U2dtBsRToIgqWtDm6xyRCoSbi5GUrJu4uCoI7n5\ + +Os0IoV5IopERnHl2joPb1ehnSPE9nA1rtXi6w4RUg0h/F4bVEgzMpBouvPKKBCmJeO\ + fK/YnOzDcoRkSqb4fHeGrNcDK+KGISFKUW/K3aaqRyFkVcmtuOFPt05/2f2vzTnnJwA\ + AAP//AwBSGHacwgAAAA==\"]" + textDecoded: + data: + currentUser: + codySubscription: + applyProRateLimits: true + currentPeriodEndAt: 2024-09-14T22:11:32Z + currentPeriodStartAt: 2024-08-14T22:11:32Z + plan: PRO + status: ACTIVE + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:21 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.754Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 6a51b119e57625053c2be57eece7c6cb + _order: 0 + cache: {} + request: + bodySize: 144 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "144" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 315 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query Repository($name: String!) { + repository(name: $name) { + id + } + } + variables: + name: github.com/sourcegraph/cody + queryString: + - name: Repository + value: null + url: https://sourcegraph.com/.api/graphql?Repository + response: + bodySize: 126 + content: + encoding: base64 + mimeType: application/json + size: 126 + text: "[\"H4sIAAAAAAAAA6pWSkks\",\"SVSyqlYqSi3IL84syS+qBPEyU5SslEJzw8qTjP0KUtwt\ + K1ND8o18Q3wr/UJ8K/0dbW2VamtrAQAAAP//\",\"AwDHAhygPQAAAA==\"]" + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:21 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:21.627Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + - _id: 2aa42833ae189b030c5bc322f1d27b0c + _order: 0 + cache: {} + request: + bodySize: 101 + cookies: [] + headers: + - _fromType: array + name: authorization + value: token + REDACTED_d5e0f0a37c9821e856b923fe14e67a605e3f6c0a517d5a4f46a4e35943ee0f6d + - _fromType: array + name: content-type + value: application/json; charset=utf-8 + - _fromType: array + name: user-agent + value: memory-usage / v1 + - _fromType: array + name: accept + value: "*/*" + - _fromType: array + name: content-length + value: "101" + - _fromType: array + name: accept-encoding + value: gzip,deflate + - name: host + value: sourcegraph.com + headersSize: 323 + httpVersion: HTTP/1.1 + method: POST + postData: + mimeType: application/json; charset=utf-8 + params: [] + textJSON: + query: |- + + query SiteProductVersion { + site { + productVersion + } + } + variables: {} + queryString: + - name: SiteProductVersion + value: null + url: https://sourcegraph.com/.api/graphql?SiteProductVersion + response: + bodySize: 136 + content: + encoding: base64 + mimeType: application/json + size: 136 + text: "[\"H4sIAAAAAAAAA6pWSkksSVSyqlYqzixJBdEFRfkppcklYalFxZn5eUpWSkYWlhbmBvFGB\ + kYmugaWugYm8aZ6ZrrJaZZGBkmm5kmpiQZKtbW1AAAAAP//AwCD4KOLSQAAAA==\"]" + textDecoded: + data: + site: + productVersion: 289870_2024-09-04_5.6-cf920b57bea0 + cookies: [] + headers: + - name: date + value: Wed, 04 Sep 2024 10:54:20 GMT + - name: content-type + value: application/json + - name: transfer-encoding + value: chunked + - name: connection + value: keep-alive + - name: access-control-allow-credentials + value: "true" + - name: access-control-allow-origin + value: "" + - name: cache-control + value: no-cache, max-age=0 + - name: vary + value: Cookie,Accept-Encoding,Authorization,Cookie, Authorization, + X-Requested-With,Cookie + - name: x-content-type-options + value: nosniff + - name: x-frame-options + value: DENY + - name: x-xss-protection + value: 1; mode=block + - name: strict-transport-security + value: max-age=31536000; includeSubDomains; preload + - name: content-encoding + value: gzip + headersSize: 1333 + httpVersion: HTTP/1.1 + redirectURL: "" + status: 200 + statusText: OK + startedDateTime: 2024-09-04T10:54:20.402Z + time: 0 + timings: + blocked: -1 + connect: -1 + dns: -1 + receive: 0 + send: 0 + ssl: -1 + wait: 0 + pages: [] + version: "1.2" diff --git a/agent/src/TestClient.ts b/agent/src/TestClient.ts index b0f69cce6d08..ec795717bdcc 100644 --- a/agent/src/TestClient.ts +++ b/agent/src/TestClient.ts @@ -145,7 +145,7 @@ export class TestClient extends MessageHandler { bin === 'node' ? [ '--enable-source-maps', - // '--expose-gc', // Uncoment when running memory.test.ts + '--expose-gc', // Uncoment when running memory.test.ts agentScript, 'api', 'jsonrpc-stdio', diff --git a/agent/src/memory.test.ts b/agent/src/memory.test.ts index d8e40805e9a7..adc832b2e976 100644 --- a/agent/src/memory.test.ts +++ b/agent/src/memory.test.ts @@ -1,5 +1,8 @@ +import fs from 'node:fs' import path from 'node:path' +import { applyPatch } from 'fast-myers-diff' import { afterAll, beforeAll, describe, it } from 'vitest' +import * as vscode from 'vscode' import { TESTING_CREDENTIALS } from '../../vscode/src/testutils/testing-credentials' import { TestClient } from './TestClient' import { TestWorkspace } from './TestWorkspace' @@ -7,7 +10,7 @@ import { TestWorkspace } from './TestWorkspace' // Disabled because we don't need to run this test in CI on every PR. We're // keeping the test around because it has useful infrastructure to debug memory // leaks. -describe.skip('Memory Usage', () => { +describe('Memory Usage', () => { const workspace = new TestWorkspace(path.join(__dirname, '__tests__', 'example-ts')) const client = TestClient.create({ workspaceRootUri: workspace.rootUri, @@ -23,42 +26,69 @@ describe.skip('Memory Usage', () => { afterAll(async () => { await workspace.afterAll() await client.afterAll() - }) + }, 20_000) - it('selection', async () => { - const uri = workspace.file('src', 'animal.ts') - await client.openFile(uri) - const document = client.workspace.getDocument(uri)! - for (let i = 0; i < 5_000; i++) { - client.notify('textDocument/didChange', { - uri: document.uri.toString(), - selection: { - start: { line: 0, character: 0 }, - end: { line: 1, character: document.lineAt(1).text.length }, - }, - }) + async function applyEvent(event: EditorEvent): Promise { + if (event.eventType === 'initialize') { + return + } + if (!event.uri) { + return + } + const uri = vscode.Uri.parse(event.uri) + + if (event.eventType === 'document/didOpen' || event.eventType === 'document/wasOpen') { + const content: string = JSON.parse(event.json ?? '{}')?.content ?? '' + await client.openFile(uri, { text: content }) + return + } + + if (event.eventType === 'document/didClose') { + client.notify('textDocument/didClose', { uri: event.uri }) + return } - const { usage: usage1 } = await client.request('testing/memoryUsage', null) - console.log(usage1) - for (let i = 0; i < 40_000; i++) { - client.notify('textDocument/didChange', { - uri: document.uri.toString(), - selection: { - start: { line: 0, character: 0 }, - end: { line: 1, character: document.lineAt(1).text.length }, - }, + + if (event.eventType === 'document/didChange') { + const document = client.workspace.getDocument(uri) + if (!document) { + throw new Error(`Document ${uri} not found`) + } + const contentChanges: [number, number, string][] = + JSON.parse(event.json ?? '{}')?.changes ?? [] + const newText = [...applyPatch(document.content, contentChanges)].join('') + await client.changeFile(uri, { + text: newText, }) - await client.request('testing/awaitPendingPromises', null) } - await new Promise(resolve => setTimeout(resolve, 3_000)) + } - const { usage: usage2 } = await client.request('testing/memoryUsage', null) - console.log(usage2) - console.log({ - diffHeapUsaged: prettyPrintBytes(usage2.heapUsed - usage1.heapUsed), - diffExternal: prettyPrintBytes(usage2.external - usage1.external), - }) - }, 20_000) + it('replay', async () => { + const dir = path.join(__dirname, '..', 'dist', 'replays') + const memory1 = await client.request('testing/memoryUsage', null) + let remainingReplays = 2 + for (const file of fs.readdirSync(dir)) { + remainingReplays-- + if (remainingReplays < 0) { + break + } + const absolutePath = path.join(dir, file) + const events = parseEditorEvents(absolutePath) + if (events.length !== 5000) { + continue + } + for (const [index, event] of events.entries()) { + await applyEvent(event) + if (index % 50 === 0) { + const memory2 = await client.request('testing/memoryUsage', null) + console.log({ + totalEvents: index, + heapUsed: prettyPrintBytes(memory2.usage.heapUsed - memory1.usage.heapUsed), + external: prettyPrintBytes(memory2.usage.external - memory1.usage.external), + }) + } + } + } + }, 120_000) }) function prettyPrintBytes(bytes: number): string { @@ -72,3 +102,48 @@ function prettyPrintBytes(bytes: number): string { return `${bytes.toFixed(2)} ${units[unitIndex]}` } + +interface EditorEvent { + readonly timestamp: string + readonly eventType: + | 'initialize' + | 'document/wasOpen' + | 'document/didOpen' + | 'document/didClose' + | 'document/didSave' + | 'document/didFocus' + | 'document/didChange' + | 'selection/didChange' + | 'visibleRanges/didChange' + | 'diagnostics/didChange' + | 'unknown' + readonly uri?: string + readonly languageId?: string + + /** String-encoded JSON object of the relevant metadata. + * For example, see SelectionInfos. */ + readonly json?: string + recordName?: string // Intentionally mutable +} + +function parseEditorEvents(file: string): EditorEvent[] { + // Parses the output of `cody-nes convert-to-json`. + const json: string[][] = JSON.parse(fs.readFileSync(file, 'utf8')) + const result: EditorEvent[] = [] + for (const row of json) { + const [timestamp, eventType, uri, languageId, json] = row + if (timestamp === 'TIMESTAMP') { + // header row + continue + } + const event: EditorEvent = { + timestamp, + eventType: eventType as EditorEvent['eventType'], + uri, + languageId, + json, + } + result.push(event) + } + return result +} diff --git a/vscode/src/main.ts b/vscode/src/main.ts index 7bdb4bb2b3dd..ff4dc1a3b337 100644 --- a/vscode/src/main.ts +++ b/vscode/src/main.ts @@ -178,7 +178,7 @@ const register = async ( disposables.push(await initVSCodeGitApi()) initWorkspaceReposMonitor(disposables) - registerParserListeners(disposables) + // registerParserListeners(disposables) registerChatListeners(disposables) // Initialize external services @@ -328,7 +328,7 @@ async function initializeSingletons( } // Registers listeners to trigger parsing of visible documents -function registerParserListeners(disposables: vscode.Disposable[]) { +export function registerParserListeners(disposables: vscode.Disposable[]) { void parseAllVisibleDocuments() disposables.push(vscode.window.onDidChangeVisibleTextEditors(parseAllVisibleDocuments)) disposables.push(vscode.workspace.onDidChangeTextDocument(updateParseTreeOnEdit))