Skip to content
Draft
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
4 changes: 2 additions & 2 deletions docs/api/cozy-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ Retrieve intance info like context, uuid, disk usage etc

*Defined in*

[packages/cozy-client/src/hooks/useQuery.js:93](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/hooks/useQuery.js#L93)
[packages/cozy-client/src/hooks/useQuery.js:94](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/hooks/useQuery.js#L94)

***

Expand All @@ -999,7 +999,7 @@ Fetches a queryDefinition and returns the queryState

*Defined in*

[packages/cozy-client/src/hooks/useQuery.js:28](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/hooks/useQuery.js#L28)
[packages/cozy-client/src/hooks/useQuery.js:29](https://github.com/cozy/cozy-client/blob/master/packages/cozy-client/src/hooks/useQuery.js#L29)

***

Expand Down
1 change: 1 addition & 0 deletions docs/api/cozy-client/classes/CozyClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Cozy-Client will automatically call `this.login()` if provided with a token and
| `autoHydrate` | `boolean` | - |
| `backgroundFetching` | `boolean` | If set to true, backgroundFetching will be enabled by default on every query. Meaning that, when the fetchStatus has already been loaded, it won't be updated during future fetches. Instead, a `isFetching` attribute will be used to indicate when background fetching is started. |
| `client` | `any` | - |
| `forceHydratation` | `boolean` | If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc. |
| `oauth` | `any` | - |
| `onError` | `Function` | Default callback if a query is errored |
| `onTokenRefresh` | `Function` | - |
Expand Down
1 change: 1 addition & 0 deletions packages/cozy-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"dependencies": {
"@cozy/minilog": "1.0.0",
"@fastify/deepmerge": "2.0.2",
"@types/jest": "^26.0.20",
"@types/lodash": "^4.14.170",
"btoa": "^1.2.1",
Expand Down
103 changes: 85 additions & 18 deletions packages/cozy-client/src/CozyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const DOC_UPDATE = 'update'
* @property {import("./types").ClientCapabilities} [capabilities] - Capabilities sent by the stack
* @property {boolean} [store] - If set to false, the client will not instantiate a Redux store automatically. Use this if you want to merge cozy-client's store with your own redux store. See [here](https://docs.cozy.io/en/cozy-client/react-integration/#1b-use-your-own-redux-store) for more information.
* @property {import('./performances/types').PerformanceAPI} [performanceApi] - The performance API that can be used to measure performances
* @property {boolean} [forceHydratation] - If set to true, all documents will be hydrated w.r.t. the provided schema's relationships, even if the relationship does not exist on the doc.
*/

/**
Expand Down Expand Up @@ -473,6 +474,7 @@ class CozyClient {
}

async _login(options) {
console.log('🟥 login 1')
this.emit('beforeLogin')

this.registerClientOnLinks()
Expand All @@ -486,18 +488,21 @@ class CozyClient {
}
}

console.log('🟥 login 2')
for (const link of this.links) {
if (link.onLogin) {
await link.onLogin()
}
}
console.log('🟥 login 3')

this.isLogged = true
this.isRevoked = false

if (this.stackClient instanceof OAuthClient) {
await this.loadInstanceOptionsFromStack()
}
console.log('🟥 login 4')

this.emit('login')
}
Expand Down Expand Up @@ -925,6 +930,8 @@ client.query(Q('io.cozy.bills'))`)
* @returns {Promise<import("./types").QueryResult>}
*/
async query(queryDefinition, { update, executeFromStore, ...options } = {}) {
console.log('⏰query 1')
const beginPrepare = performance.now()
const markQuery = this.performanceApi.mark(
`client.query(${queryDefinition.doctype})`
)
Expand Down Expand Up @@ -975,21 +982,28 @@ client.query(Q('io.cozy.bills'))`)
executeQueryFromState(this.store.getState(), queryDefinition)
)
: () => this.requestQuery(queryDefinition, options)
const endPrepare = performance.now()
console.log('🍺 CozyClient query prepare took', (endPrepare - beginPrepare), 'ms')
const beginExec = performance.now()
const response = await this._promiseCache.exec(requestFn, () =>
stringify(queryDefinition)
)
const endExec = performance.now()
console.log('🍺 CozyClient query exec took', (endExec - beginExec), 'ms')

const beginReceive = performance.now()
const queryReceivedResult = receiveQueryResult(queryId, response, {
update,
backgroundFetching
})
const endReceive = performance.now()
console.log('🍺 CozyClient query receiveQueryResult took', (endReceive - beginReceive), 'ms')
const beginDispatch = performance.now()
this.dispatch(
receiveQueryResult(queryId, response, {
update,
backgroundFetching
})
queryReceivedResult
)
this.performanceApi.measure({
markName: markQuery,
measureName: `${markQuery} success`,
color: 'primary'
})
const endDispatch = performance.now()
console.log('🍺 CozyClient query Dispatch took', (endDispatch - beginDispatch), 'ms')
return response
} catch (error) {
this.performanceApi.measure({
Expand Down Expand Up @@ -1024,10 +1038,14 @@ client.query(Q('io.cozy.bills'))`)
options.as || this.queryIdGenerator.generateId(queryDefinition)
const mergedOptions = { ...options, as: queryId }
try {
const begin = performance.now()
let resp = await this.query(queryDefinition, mergedOptions)
const end = performance.now()
console.log('🍺 first query took', (end - begin), 'ms')
const documents = resp.data

while (resp && resp.next) {
console.log('🍺 while')
if (resp.bookmark) {
resp = await this.query(
queryDefinition.offsetBookmark(resp.bookmark),
Expand All @@ -1045,6 +1063,7 @@ client.query(Q('io.cozy.bills'))`)
}
documents.push(...resp.data)
}
console.log('🍺 return docs')
return documents
} catch (e) {
logger.log(`queryAll error for ${e.toString()}`)
Expand Down Expand Up @@ -1110,17 +1129,27 @@ client.query(Q('io.cozy.bills'))`)
* @returns {Promise<import("./types").ClientResponse>}
*/
async requestQuery(definition, options) {
const begin = performance.now()
const mainResponse = await this.chain.request(definition, options)
const end = performance.now()
console.log('🛋️ CozyClient chain.request took', (end - begin), 'ms')

const begin2 = performance.now()
await this.persistVirtualDocuments(definition, mainResponse.data)
const end2 = performance.now()
console.log('🛋️ CozyClient persistVirtualDocuments took', (end2 - begin2), 'ms')

if (!definition.includes) {
console.log('🛋️ CozyClient return no-includes')
return mainResponse
}
const begin3 = performance.now()
const withIncluded = await this.fetchRelationships(
mainResponse,
this.getIncludesRelationships(definition)
)
const end3 = performance.now()
console.log('🛋️ CozyClient fetchRelationships took', (end3 - begin3), 'ms')
return withIncluded
}

Expand Down Expand Up @@ -1156,7 +1185,18 @@ client.query(Q('io.cozy.bills'))`)
if (!Array.isArray(data)) {
await this.persistVirtualDocument(data, enforce)
} else {
for (const document of data) {
const documentsToPersist = data.filter(document => {
if (!document || document.cozyLocalOnly) {
return false
}

if ((!document.meta?.rev && !document._rev) || enforce) {
return true
}

return false
})
for (const document of documentsToPersist) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it really better? OK you don't call a method if not needed, but now you loop twice

Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually significantly better. After a big query retrieving 25K docs, the persistVirtualDocuments takes:

  • Before: 609ms
  • After: 20ms

I wonder if this is because of the event loop overwhelmed by too many calls

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh shit, I didn't saw that the persistVirtualDocument was async... This is why.

nit: You can now remove the check within the persistVirtualDocument method since should only call it with the docs to persist ^^.

await this.persistVirtualDocument(document, enforce)
}
}
Expand All @@ -1176,6 +1216,7 @@ client.query(Q('io.cozy.bills'))`)
}

if ((!document.meta?.rev && !document._rev) || enforce) {
console.log('🚨🚨🚨🚨 PERSIST')
await this.chain.persistCozyData(document)
}
}
Expand Down Expand Up @@ -1216,7 +1257,7 @@ client.query(Q('io.cozy.bills'))`)
if (queryDef instanceof QueryDefinition) {
definitions.push(queryDef)
} else {
documents.push(queryDef)
documents.push(doc)
}
} catch {
// eslint-disable-next-line
Expand Down Expand Up @@ -1332,9 +1373,11 @@ client.query(Q('io.cozy.bills'))`)

hydrateRelationships(document, schemaRelationships) {
const methods = this.getRelationshipStoreAccessors()
return mapValues(schemaRelationships, (assoc, name) =>
createAssociation(document, assoc, methods)
)
return mapValues(schemaRelationships, (assoc, name) => {
if (this.options?.forceHydratation || document.relationships?.[assoc]) {
return createAssociation(document, assoc, methods)
}
})
}

/**
Expand Down Expand Up @@ -1444,13 +1487,29 @@ client.query(Q('io.cozy.bills'))`)
return queryResults
}

const data =
const hydratedData =
hydrated && doctype
? this.hydrateDocuments(doctype, queryResults.data)
: queryResults.data

const relationships = this.schema.getDoctypeSchema(doctype)?.relationships
const relationshipNames = relationships
? Object.keys(relationships)
: null

// The `data` array contains the hydrated data with the relationships, if any.
// The `storeData` array contains the documents from the store: this is useful to preserve
// referential equality, to be later evaluated to determine whether or not the
// documents had changed.
return {
...queryResults,
data: isSingleDocQuery && singleDocData ? data[0] : data
data:
isSingleDocQuery && singleDocData ? hydratedData[0] : hydratedData,
storeData:
isSingleDocQuery && singleDocData
? queryResults.data[0]
: queryResults.data,
relationshipNames
}
} catch (e) {
logger.warn(
Expand Down Expand Up @@ -1784,12 +1843,20 @@ instantiation of the client.`
* @returns {Promise<void>}
*/
async loadInstanceOptionsFromStack() {
console.log('🔰 loadInstanceOptionsFromStack 1')
const results = await Promise.all([
this.query(
Q('io.cozy.settings').getById('io.cozy.settings.capabilities')
),
this.query(Q('io.cozy.settings').getById('io.cozy.settings.instance'))
).catch(error => {
console.log(error)
throw error
}),
this.query(Q('io.cozy.settings').getById('io.cozy.settings.instance')).catch(error => {
console.log(error)
throw error
})
])
console.log('🔰 loadInstanceOptionsFromStack 2')

const { data: capabilitiesData } = results[0]
const { data: instanceData } = results[1]
Expand Down
3 changes: 3 additions & 0 deletions packages/cozy-client/src/StackLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ export default class StackLink extends CozyLink {
}

async request(operation, options, result, forward) {
console.log('stack request')
if (!options?.forceStack && this.isOnline && !(await this.isOnline())) {
console.log('forward')
return forward(operation, options)
}

Expand All @@ -108,6 +110,7 @@ export default class StackLink extends CozyLink {
}

async persistCozyData(data, forward) {
console.log('persistCozyData from StackLink')
return forward(data)
}
/**
Expand Down
1 change: 1 addition & 0 deletions packages/cozy-client/src/WebFlagshipLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class WebFlagshipLink extends CozyLink {
}

async persistCozyData(data, forward) {
console.log('persistCozyData from WebFlagshipLink')
// Persist data should do nothing here as data is already persisted on Flagship side
return
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cozy-client/src/hooks/useQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useClient from './useClient'
import logger from '../logger'
import { clientContext } from '../context'
import { QueryDefinition } from '../queries/dsl'
import { equalityCheckForQuery } from './utils'

const useSelector = createSelectorHook(clientContext)

Expand Down Expand Up @@ -61,7 +62,7 @@ const useQuery = (queryDefinition, options) => {
hydrated: get(options, 'hydrated', true),
singleDocData: get(options, 'singleDocData', false)
})
})
}, equalityCheckForQuery)

useEffect(
() => {
Expand Down
Loading