11import request from 'request' ;
22import fs from 'fs' ;
33import yaml from 'js-yaml' ;
4- import merge from 'lodash/merge' ;
4+ import { merge , findIndex } from 'lodash' ;
5+ import { normalizeDelta , findInstance } from './deltaUtils' ;
56import path from 'path' ;
67import { series , waterfall } from 'async' ;
78import { EventEmitter } from 'events' ;
@@ -69,6 +70,8 @@ export default class Eureka extends EventEmitter {
6970
7071 this . requestMiddleware = this . config . requestMiddleware ;
7172
73+ this . hasFullRegistry = false ;
74+
7275 if ( this . amazonDataCenter ) {
7376 this . metadataClient = new AwsMetadata ( {
7477 logger : this . logger ,
@@ -328,32 +331,72 @@ export default class Eureka extends EventEmitter {
328331 }
329332
330333 /*
331- Retrieves all applications registered with the Eureka server
334+ Orchestrates fetching registry
332335 */
333336 fetchRegistry ( callback = noop ) {
337+ if ( this . config . shouldUseDelta && this . hasFullRegistry ) {
338+ this . fetchDelta ( callback ) ;
339+ } else {
340+ this . fetchFullRegistry ( callback ) ;
341+ }
342+ }
343+
344+ /*
345+ Retrieves all applications registered with the Eureka server
346+ */
347+ fetchFullRegistry ( callback = noop ) {
334348 this . eurekaRequest ( {
335349 uri : '' ,
336350 headers : {
337351 Accept : 'application/json' ,
338352 } ,
339353 } , ( error , response , body ) => {
340354 if ( ! error && response . statusCode === 200 ) {
341- this . logger . debug ( 'retrieved registry successfully' ) ;
355+ this . logger . debug ( 'retrieved full registry successfully' ) ;
342356 try {
343357 this . transformRegistry ( JSON . parse ( body ) ) ;
344358 } catch ( ex ) {
345359 return callback ( ex ) ;
346360 }
347361 this . emit ( 'registryUpdated' ) ;
362+ this . hasFullRegistry = true ;
348363 return callback ( null ) ;
349364 } else if ( error ) {
350365 this . logger . warn ( 'Error fetching registry' , error ) ;
351366 return callback ( error ) ;
352367 }
353- callback ( new Error ( 'Unable to retrieve registry from Eureka server' ) ) ;
368+ callback ( new Error ( 'Unable to retrieve full registry from Eureka server' ) ) ;
354369 } ) ;
355370 }
356371
372+ /*
373+ Retrieves all applications registered with the Eureka server
374+ */
375+ fetchDelta ( callback = noop ) {
376+ this . eurekaRequest ( {
377+ uri : 'delta' ,
378+ headers : {
379+ Accept : 'application/json' ,
380+ } ,
381+ } , ( error , response , body ) => {
382+ if ( ! error && response . statusCode === 200 ) {
383+ this . logger . debug ( 'retrieved delta successfully' ) ;
384+ let applications ;
385+ try {
386+ const jsonBody = JSON . parse ( body ) ;
387+ applications = jsonBody . applications . application ;
388+ this . handleDelta ( this . cache , applications ) ;
389+ return callback ( null ) ;
390+ } catch ( ex ) {
391+ return callback ( ex ) ;
392+ }
393+ } else if ( error ) {
394+ this . logger . warn ( 'Error fetching delta registry' , error ) ;
395+ return callback ( error ) ;
396+ }
397+ callback ( new Error ( 'Unable to retrieve delta registry from Eureka server' ) ) ;
398+ } ) ;
399+ }
357400 /*
358401 Transforms the given registry and caches the registry locally
359402 */
@@ -382,24 +425,11 @@ export default class Eureka extends EventEmitter {
382425 */
383426 transformApp ( app , cache ) {
384427 if ( app . instance . length ) {
385- const instances = app . instance . filter ( ( instance ) => ( this . validateInstance ( instance ) ) ) ;
386- cache . app [ app . name . toUpperCase ( ) ] = instances ;
387- instances . forEach ( ( inst ) => {
388- const vipAddresses = this . splitVipAddress ( inst . vipAddress ) ;
389- vipAddresses . forEach ( ( vipAddress ) => {
390- if ( ! cache . vip [ vipAddress ] ) {
391- cache . vip [ vipAddress ] = [ ] ;
392- }
393- cache . vip [ vipAddress ] . push ( inst ) ;
394- } ) ;
395- } ) ;
428+ app . instance
429+ . filter ( this . validateInstance . bind ( this ) )
430+ . forEach ( ( inst ) => this . addInstance ( cache , inst ) ) ;
396431 } else if ( this . validateInstance ( app . instance ) ) {
397- const instances = [ app . instance ] ;
398- const vipAddresses = this . splitVipAddress ( app . instance . vipAddress ) ;
399- vipAddresses . forEach ( ( vipAddress ) => {
400- cache . vip [ vipAddress ] = instances ;
401- } ) ;
402- cache . app [ app . name . toUpperCase ( ) ] = instances ;
432+ this . addInstance ( cache , app . instance ) ;
403433 }
404434 }
405435
@@ -421,6 +451,63 @@ export default class Eureka extends EventEmitter {
421451 return vipAddress . split ( ',' ) ;
422452 }
423453
454+ handleDelta ( cache , appDelta ) {
455+ const delta = normalizeDelta ( appDelta ) ;
456+ delta . forEach ( ( app ) => {
457+ app . instance . forEach ( ( instance ) => {
458+ switch ( instance . actionType ) {
459+ case 'ADDED' : this . addInstance ( cache , instance ) ; break ;
460+ case 'MODIFIED' : this . modifyInstance ( cache , instance ) ; break ;
461+ case 'DELETED' : this . deleteInstance ( cache , instance ) ; break ;
462+ default : this . logger . warn ( 'Unknown delta actionType' , instance . actionType ) ; break ;
463+ }
464+ } ) ;
465+ } ) ;
466+ }
467+
468+ addInstance ( cache , instance ) {
469+ if ( ! this . validateInstance ( instance ) ) return ;
470+ const vipAddresses = this . splitVipAddress ( instance . vipAddress ) ;
471+ const appName = instance . app . toUpperCase ( ) ;
472+ vipAddresses . forEach ( ( vipAddress ) => {
473+ const alreadyContains = findIndex ( cache . vip [ vipAddress ] , findInstance ( instance ) ) > - 1 ;
474+ if ( alreadyContains ) return ;
475+ if ( ! cache . vip [ vipAddress ] ) {
476+ cache . vip [ vipAddress ] = [ ] ;
477+ }
478+ cache . vip [ vipAddress ] . push ( instance ) ;
479+ } ) ;
480+ if ( ! cache . app [ appName ] ) cache . app [ appName ] = [ ] ;
481+ const alreadyContains = findIndex ( cache . app [ appName ] , findInstance ( instance ) ) > - 1 ;
482+ if ( alreadyContains ) return ;
483+ cache . app [ appName ] . push ( instance ) ;
484+ }
485+
486+ modifyInstance ( cache , instance ) {
487+ if ( ! this . validateInstance ( instance ) ) return ;
488+ const vipAddresses = this . splitVipAddress ( instance . vipAddress ) ;
489+ const appName = instance . app . toUpperCase ( ) ;
490+ vipAddresses . forEach ( ( vipAddress ) => {
491+ const index = findIndex ( cache . vip [ vipAddress ] , findInstance ( instance ) ) ;
492+ if ( index > - 1 ) cache . vip [ vipAddress ] . splice ( index , 1 , instance ) ;
493+ else this . addInstance ( cache , instance ) ;
494+ } ) ;
495+ const index = findIndex ( cache . app [ appName ] , findInstance ( instance ) ) ;
496+ if ( index > - 1 ) cache . app [ appName ] . splice ( cache . vip [ instance . vipAddress ] , 1 , instance ) ;
497+ else this . addInstance ( cache , instance ) ;
498+ }
499+
500+ deleteInstance ( cache , instance ) {
501+ const vipAddresses = this . splitVipAddress ( instance . vipAddress ) ;
502+ const appName = instance . app . toUpperCase ( ) ;
503+ vipAddresses . forEach ( ( vipAddress ) => {
504+ const index = findIndex ( cache . vip [ vipAddress ] , findInstance ( instance ) ) ;
505+ if ( index > - 1 ) cache . vip [ vipAddress ] . splice ( index , 1 ) ;
506+ } ) ;
507+ const index = findIndex ( cache . app [ appName ] , findInstance ( instance ) ) ;
508+ if ( index > - 1 ) cache . app [ appName ] . splice ( cache . vip [ instance . vipAddress ] , 1 ) ;
509+ }
510+
424511 /*
425512 Fetches the metadata using the built-in client and updates the instance
426513 configuration with the hostname and IP address. If the value of the config
0 commit comments