@@ -18,6 +18,7 @@ import {isEqual, isObject} from 'lodash'
1818
1919import { Rule } from '../legacy/Rule'
2020import { OWN_PROPS_NAME } from '../legacy/types/constants'
21+ import { IdleScheduler , type Scheduler , SYNC_SCHEDULER } from './scheduler'
2122import {
2223 type ArrayElement ,
2324 type ArrayTypeDef ,
@@ -59,18 +60,59 @@ export class DescriptorConverter {
5960 *
6061 * This is automatically cached in a weak map.
6162 */
62- async get ( schema : Schema ) : Promise < SetSynchronization < RegistryType > > {
63+ async get (
64+ schema : Schema ,
65+ opts ?: {
66+ /**
67+ * If present, this will use an idle scheduler which records duration into this array.
68+ * This option will be ignored if the `scheduler` option is passed in.
69+ **/
70+ pauseDurations ?: number [ ]
71+
72+ /** An explicit scheduler to do the work. */
73+ scheduler ?: Scheduler
74+ } ,
75+ ) : Promise < SetSynchronization < RegistryType > > {
76+ /*
77+ Converting the schema into a descriptor consists of two parts:
78+
79+ 1. Traversing the type into a descriptor.
80+ 2. Serializing the descriptor, including SHA256 hashing.
81+
82+ Note that only (2) can be done in a background worker since the type
83+ itself isn't serializable (which is a requirement for a background
84+ worker). In addition, we expect (2) to scale in the same way as (1): If it
85+ takes X milliseconds to traverse the type into a descriptor it will
86+ probably take c*X milliseconds to serialize it.
87+
88+ This means that a background worker actually doesn't give us that much
89+ value. A huge type will either way be expensive to convert from a type to
90+ a descriptor. Therefore this function currently only avoid blocking by
91+ only processing each type separately.
92+
93+ If we want to minimize the blocking further we would have to restructure
94+ this converter to be able to convert the types asynchronously and _then_
95+ it might make sense to the serialization step itself in a background
96+ worker.
97+ */
6398 let value = this . cache . get ( schema )
6499 if ( value ) return value
65100
101+ let idleScheduler : IdleScheduler | undefined
102+ const scheduler =
103+ opts ?. scheduler ||
104+ ( opts ?. pauseDurations
105+ ? ( idleScheduler = new IdleScheduler ( opts . pauseDurations ) )
106+ : SYNC_SCHEDULER )
107+
66108 const options : Options = {
67109 fields : new Map ( ) ,
68110 duplicateFields : new Map ( ) ,
69111 arrayElements : new Map ( ) ,
70112 duplicateArrayElements : new Map ( ) ,
71113 }
72114
73- const namedTypes = schema . getLocalTypeNames ( ) . map ( ( name ) => {
115+ const namedTypes = await scheduler . map ( schema . getLocalTypeNames ( ) , ( name ) => {
74116 const typeDef = convertTypeDef ( schema . get ( name ) ! , name , options )
75117 return { name, typeDef}
76118 } )
@@ -89,24 +131,27 @@ export class DescriptorConverter {
89131 const builder = new SetBuilder ( { rewriteMap} )
90132
91133 // Now we can build the de-duplicated objects:
92- for ( const [ fieldDef , key ] of options . duplicateFields . entries ( ) ) {
134+ await scheduler . forEachIter ( options . duplicateFields . entries ( ) , ( [ fieldDef , key ] ) => {
93135 builder . addObject ( 'sanity.schema.hoisted' , { key, value : { ...fieldDef } } )
94- }
136+ } )
95137
96- for ( const [ arrayElem , key ] of options . duplicateArrayElements . entries ( ) ) {
138+ await scheduler . forEachIter ( options . duplicateArrayElements . entries ( ) , ( [ arrayElem , key ] ) => {
97139 builder . addObject ( 'sanity.schema.hoisted' , { key, value : { ...arrayElem } } )
98- }
140+ } )
99141
100- for ( const namedType of namedTypes ) {
142+ await scheduler . forEach ( namedTypes , ( namedType ) => {
101143 builder . addObject ( 'sanity.schema.namedType' , namedType )
102- }
144+ } )
103145
104146 if ( schema . parent ) {
105- builder . addSet ( await this . get ( schema . parent ) )
147+ builder . addSet ( await this . get ( schema . parent , { scheduler } ) )
106148 }
107149
108150 value = builder . build ( 'sanity.schema.registry' )
109151 this . cache . set ( schema , value )
152+
153+ // If we created the scheduler we also need to end it.
154+ if ( idleScheduler ) idleScheduler . end ( )
110155 return value
111156 }
112157}
0 commit comments