Skip to content

Commit 04b425e

Browse files
authored
Move Upload File to Cloud logic to Vue (#14107)
A prelude to #13491 Move upload file logic to Vue. It will be needed when handling hybrid projects. # Important Notes Because of various problems with `tanstack` query we faced lately, which were hard to debug, I decided to _not_ use it in the new implementation. I moved the "pooling" mechanism to the new store.
1 parent 45720d3 commit 04b425e

File tree

17 files changed

+447
-350
lines changed

17 files changed

+447
-350
lines changed

app/common/src/queryClient.ts

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,8 @@ import type { AsyncStorage, StoragePersisterOptions } from '@tanstack/query-pers
99
import { experimental_createPersister as createPersister } from '@tanstack/query-persist-client-core'
1010
import * as vueQuery from '@tanstack/vue-query'
1111
import { toRaw } from 'vue'
12-
import { ConditionVariable } from './utilities/ConditionVariable'
1312
import { useCallbackRegistry } from './utilities/data/callbacks'
1413
import { cloneDeepUnref } from './utilities/data/reactive'
15-
16-
/** An enumeration of all mutation pool ids. */
17-
export interface MutationPools {
18-
// Required otherwise in this module there are no keys, and `pools[poolMeta.id]` below becomes
19-
// `never`.
20-
readonly [DUMMY_MUTATION_POOL_SYMBOL]: true
21-
}
22-
declare const DUMMY_MUTATION_POOL_SYMBOL: unique symbol
23-
24-
/**
25-
* Declaration merge into `MutationPools` to add a new mutation pool id:
26-
*
27-
* ```ts
28-
* declare module 'enso-common/src/queryClient' {
29-
* interface MutationPools {
30-
* myNewPoolId: true
31-
* }
32-
* }
33-
* ```
34-
*/
35-
export type MutationPoolId = keyof MutationPools
36-
3714
declare module '@tanstack/query-core' {
3815
/** Query client with additional methods. */
3916
interface QueryClient {
@@ -65,10 +42,6 @@ declare module '@tanstack/query-core' {
6542
*/
6643
readonly awaitInvalidates?: queryCore.QueryKey[] | boolean
6744
readonly refetchType?: queryCore.InvalidateQueryFilters['refetchType']
68-
readonly pool?: {
69-
id: MutationPoolId
70-
parallelism: number
71-
}
7245
}
7346

7447
readonly queryMeta: {
@@ -172,30 +145,6 @@ function useMutationCache(): {
172145
}
173146
}
174147

175-
function useConcurrencyControl({ onMutate, onSettled }: MutationHooks) {
176-
const pools: Partial<Record<MutationPoolId, { usedLanes: number; queue: ConditionVariable }>> = {}
177-
178-
onMutate(async (_variables, mutation) => {
179-
const poolMeta = mutation.meta?.pool
180-
if (!poolMeta) {
181-
return
182-
}
183-
const poolInfo = (pools[poolMeta.id] ??= { usedLanes: 0, queue: new ConditionVariable() })
184-
if (poolInfo.usedLanes >= poolMeta.parallelism) {
185-
await poolInfo.queue.wait()
186-
}
187-
poolInfo.usedLanes += 1
188-
})
189-
onSettled(async (_data, _error, _variables, _context, mutation) => {
190-
const poolMeta = mutation.meta?.pool
191-
if (poolMeta) {
192-
const poolInfo = (pools[poolMeta.id] ??= { usedLanes: 1, queue: new ConditionVariable() })
193-
poolInfo.usedLanes -= 1
194-
while (poolInfo.usedLanes < poolMeta.parallelism && (await poolInfo.queue.notifyOne()));
195-
}
196-
})
197-
}
198-
199148
declare const brandRaw: unique symbol
200149
/**
201150
* A value that is known not to be a reactive proxy; this marker type can be used to ensure
@@ -309,7 +258,6 @@ export function createQueryClient<TStorageValue = string>(
309258
},
310259
},
311260
})
312-
useConcurrencyControl(mutationHooks)
313261
useInvalidation({ mutationHooks, queryHooks, queryClient })
314262

315263
Object.defineProperty(queryClient, 'nukePersister', {

app/common/src/services/Backend.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,11 +1959,20 @@ export default abstract class Backend {
19591959
abstract uploadFileStart(
19601960
params: UploadFileRequestParams,
19611961
file: File,
1962+
abort?: AbortSignal,
19621963
): Promise<UploadLargeFileMetadata>
19631964
/** Upload a chunk of a large file. */
1964-
abstract uploadFileChunk(url: HttpsUrl, file: Blob, index: number): Promise<S3MultipartPart>
1965+
abstract uploadFileChunk(
1966+
url: HttpsUrl,
1967+
file: Blob,
1968+
index: number,
1969+
abort?: AbortSignal,
1970+
): Promise<{ part: S3MultipartPart; size: number }>
19651971
/** Finish uploading a large file. */
1966-
abstract uploadFileEnd(body: UploadFileEndRequestBody): Promise<UploadedAsset>
1972+
abstract uploadFileEnd(
1973+
body: UploadFileEndRequestBody,
1974+
abort?: AbortSignal,
1975+
): Promise<UploadedAsset>
19671976
/** Change the name of a file. */
19681977
abstract updateFile(fileId: FileId, body: UpdateFileRequestBody, title: string): Promise<void>
19691978

@@ -2097,9 +2106,9 @@ export default abstract class Backend {
20972106
}
20982107

20992108
/** Send a binary HTTP POST request to the given path. */
2100-
protected postBinary<T = void>(path: string, payload: Blob) {
2109+
protected postBinary<T = void>(path: string, payload: Blob, options?: HttpClientPostOptions) {
21012110
return this.checkForAuthenticationError(() =>
2102-
this.client.postBinary<T>(this.resolvePath(path), payload),
2111+
this.client.postBinary<T>(this.resolvePath(path), payload, options),
21032112
)
21042113
}
21052114

app/common/src/services/HttpClient.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface ResponseWithTypedJson<U> extends Response {
1616
/** Options for {@link HttpClient['post']} method. */
1717
export interface HttpClientPostOptions {
1818
readonly keepalive?: boolean
19+
readonly abort?: AbortSignal | undefined
1920
}
2021

2122
/** Options for {@link HttpClient.request} private method. */
@@ -54,16 +55,19 @@ export class HttpClient {
5455
payload: JSON.stringify(payload),
5556
mimetype: 'application/json',
5657
keepalive: options?.keepalive ?? false,
58+
abort: options?.abort,
5759
})
5860
}
5961

6062
/** Send a base64-encoded binary HTTP POST request to the specified URL. */
61-
async postBinary<T = void>(url: string, payload: Blob) {
63+
async postBinary<T = void>(url: string, payload: Blob, options?: HttpClientPostOptions) {
6264
return await this.request<'POST', T>({
6365
method: 'POST',
6466
url,
6567
payload,
6668
mimetype: 'application/octet-stream',
69+
keepalive: options?.keepalive ?? false,
70+
abort: options?.abort,
6771
})
6872
}
6973

app/common/src/utilities/ConditionVariable.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ export class ConditionVariable {
1616
}
1717

1818
/** Resolve all promises in the queue. */
19-
notifyAll(): Promise<boolean> {
19+
notifyAll(): boolean {
2020
const success = this.resolveQueue.length !== 0
2121
for (const resolve of this.resolveQueue.splice(0, this.resolveQueue.length)) {
2222
resolve()
2323
}
2424
// Give the code after the resolved promises time to execute.
25-
return Promise.resolve(success)
25+
return success
2626
}
2727

2828
/** Resolve a single promise in the queue. */
29-
notifyOne(): Promise<boolean> {
29+
notifyOne(): boolean {
3030
const resolve = this.resolveQueue.shift()
3131
resolve?.()
3232
// Give the code after the resolved promise time to execute.
33-
return Promise.resolve(resolve != null)
33+
return resolve != null
3434
}
3535
}

0 commit comments

Comments
 (0)