DataValidator: Queue validations when incomplete
* src/validate/DataValidator.js (validate): If a validation is ongoing, queue requests. * test/validate/DataValidatorTest.js: Add test. DEV-2299master
parent
9225924e7e
commit
71024bd389
|
@ -61,6 +61,12 @@ module.exports = Class( 'DataValidator',
|
|||
*/
|
||||
'private _stores': {},
|
||||
|
||||
/**
|
||||
* Pending validation
|
||||
* @type {Promise}
|
||||
*/
|
||||
'private _pending': null,
|
||||
|
||||
|
||||
/**
|
||||
* Initialize validator
|
||||
|
@ -103,6 +109,10 @@ module.exports = Class( 'DataValidator',
|
|||
/**
|
||||
* Validate diff and update field monitor
|
||||
*
|
||||
* If a validation is pending completion, all further `#validate`
|
||||
* requests will be queued to prevent unexpected/inconsistent system
|
||||
* states and race conditions.
|
||||
*
|
||||
* The external validator `validatef` is a kluge while the system
|
||||
* undergoes refactoring.
|
||||
*
|
||||
|
@ -116,6 +126,16 @@ module.exports = Class( 'DataValidator',
|
|||
{
|
||||
const _self = this;
|
||||
|
||||
// queue requests if we're already processing
|
||||
if ( this._pending )
|
||||
{
|
||||
this._pending.then(
|
||||
() => this.validate( diff, classes, validatef )
|
||||
);
|
||||
|
||||
return this._pending;
|
||||
}
|
||||
|
||||
let failures = {};
|
||||
|
||||
if ( diff !== undefined )
|
||||
|
@ -132,8 +152,12 @@ module.exports = Class( 'DataValidator',
|
|||
}
|
||||
|
||||
// XXX: this assumes that the above is synchronous
|
||||
return this._populateStore( classes, this._stores.cstore, 'indexes' )
|
||||
.then( () => this.updateFailures( diff, failures ) );
|
||||
return this._pending =
|
||||
this._populateStore(
|
||||
classes, this._stores.cstore, 'indexes'
|
||||
)
|
||||
.then( () => this.updateFailures( diff, failures ) )
|
||||
.then( () => this._pending = null );
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -251,6 +251,71 @@ describe( 'DataValidator', () =>
|
|||
expect( cleared.b && cleared.c ).to.be.true
|
||||
);
|
||||
} ) );
|
||||
|
||||
|
||||
// otherwise system might get into an unexpected state
|
||||
it( 'queues concurrent validations', () =>
|
||||
{
|
||||
const expected_failure = {};
|
||||
|
||||
let vcalled = 0;
|
||||
|
||||
const bvalidator = createMockBucketValidator( function( _, __, ___ )
|
||||
{
|
||||
vcalled++;
|
||||
} );
|
||||
|
||||
const vmonitor = sinon.createStubInstance( ValidStateMonitor );
|
||||
const dep_factory = createMockDependencyFactory();
|
||||
const getStore = createStubStore();
|
||||
|
||||
const diff_a = { foo: [ 'a', 'b', 'c' ] };
|
||||
const diff_b = { foo: [ 'd' ] };
|
||||
|
||||
const validatef = ( diff, failures ) =>
|
||||
{
|
||||
// not a real failure; just used to transfer state to stub
|
||||
// (see below)
|
||||
failures.failedon = diff;
|
||||
};
|
||||
|
||||
return new Promise( ( accept, reject ) =>
|
||||
{
|
||||
// by the time it gets to this the second time, store could
|
||||
// be in any sort of state depending on what callbacks were
|
||||
// invoked first (implementation details)
|
||||
vmonitor.update = ( _, failures ) =>
|
||||
{
|
||||
const orig_diff = failures.failedon;
|
||||
|
||||
// if the external validator was called twice, then they
|
||||
// didn't wait for us to finish
|
||||
if ( ( orig_diff === diff_a ) && ( vcalled !== 1 ) )
|
||||
{
|
||||
reject( Error( "Request not queued" ) );
|
||||
}
|
||||
|
||||
// if this key doesn't exist, then the store has been
|
||||
// cleared (which happens before it's re-populated with
|
||||
// the new diff)
|
||||
return expect( getStore().bstore.get( 'foo' ) )
|
||||
.to.eventually.deep.equal( orig_diff.foo )
|
||||
.then( () => {
|
||||
// the second test, after which we're done
|
||||
if ( orig_diff === diff_b )
|
||||
{
|
||||
accept();
|
||||
}
|
||||
} )
|
||||
.catch( e => reject( e ) );
|
||||
};
|
||||
|
||||
const sut = Sut( bvalidator, vmonitor, dep_factory, getStore );
|
||||
|
||||
sut.validate( diff_a, {}, validatef );
|
||||
sut.validate( diff_b, {}, validatef );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue