gatekeeper par guillaume faure

Post on 06-Jan-2017

3.780 Views

Category:

Software

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Gatekeeper @ DeezerCocoheads Paris

Guillaume FaureMai 2016

Deezer

1

Concept

Qu’est qu’un GateKeeper

Permet au cours de l’exécution de l’application charger/déchargerdes modules.

2

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)

• Temps de validation appstore variable• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)• Temps de validation appstore variable

• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Pourquoi un GateKeeper

• Pas de rollout progressif (contrairement au Playstore)• Temps de validation appstore variable• Phases de beta test trop courtes / sans assez d’utilisateurs

3

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé

• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta

• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office

• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays

• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur

• Offre• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre

• Platform• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform

• Formfactor

4

Conditions d’activation

Le choix ou non de l’activation est faite coté serveur

• Employé• Utilisateur en béta• Office• Pays• Pourcentage d’utilisateur• Offre• Platform• Formfactor

4

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring

• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur

• Test utilisateurs• ...

5

Quel utilité ?

Les modules sont des pans entiers de fonctionnalités ou decomportement de l’application.

• Refactoring• Fonctionnalités utilisateur• Test utilisateurs• ...

5

Modules

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL

• Est une interface vers une fonctionalité• Les utilisateurs de cette fonctionalité appellent cette interface

6

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL• Est une interface vers une fonctionalité

• Les utilisateurs de cette fonctionalité appellent cette interface

6

Principe d’un module de gate keep

• Ce n’est pas juste un BOOL• Est une interface vers une fonctionalité• Les utilisateurs de cette fonctionalité appellent cette interface

6

Deux formes de modules

• Module simple : Le module n’est chargé que lorsque le gatekeepcorrespondant est actif.

• Module double : Le module est composé de deuximplémentations, l’une lorsque le gatekeep correspondant estactif, l’autre quand il est inactif.

7

Deux formes de modules

• Module simple : Le module n’est chargé que lorsque le gatekeepcorrespondant est actif.

• Module double : Le module est composé de deuximplémentations, l’une lorsque le gatekeep correspondant estactif, l’autre quand il est inactif.

7

Module interface

1 @interface DZRGateKeeperModule : NSObject2 + (nullable id)activatedModule;3 + (nullable id)deactivatedModule;4

5 + (nullable NSString *)name;6 + (nullable instancetype)module;7

8 - (void)moduleLoad;9 - (void)moduleUnload;

10 @end

8

Proxy

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.

• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Comment Garantir le déchargement des modules

• Il faut pouvoir charger/décharger les modules à n’ importe quelmoment de l’exécution de l’application.

• Le module, peut être appelé de n’ import où.• On ne controlle pas la politique d’ownership

ProxyUtilisation d’un NSProxy pour protéger les modules.

Le code extérieur, ne voit jamais que le proxy, jamais le moduleréel.

Seul le proxy retain l’ instance du module.

9

Fonctionnement du proxy

Le Proxy est géré par le mécanisme de GateKeeper.

L’ implementation du module renvoie son proxy lorsqu’on demandeson singleton.

1 + (instancetype)module2 {3 return [[DZRGateKeeper sharedGateKeeper] moduleWithName:[[self class]

name]];♦

↣4 }

Le proxy doit maintenant se faire passer pour une instance dumodule.

10

GateKeeper Proxy

Le proxy a besoin d’ information sur la classe qu’ il va usurper(notamment pour les modules simples).

1 @interface DZRGateKeeperModuleProxy : NSProxy2 - (id)initWithClass:(Class)moduleClass;3 @end

11

Déguisons le proxy

Il va ensuite forwarder tous les messages.

1 - (id)forwardingTargetForSelector:(SEL)aSelector2 {3 return self.module ?: (id)self;4 }

Deux possibilités

Fast path

• Première partie de laconditionnelle

• Exécuté quand le proxy disposed’un module

• On donne le module commenouvelle target du message

Slow path

• Seconde partie de laconditionnelle

• Exécuté quand le proxy disposed’aucune instance de module

• Le proxy va devoir répondre aumessage lui-même

12

Forwarding: Slow path

1 - (void)forwardInvocation:(NSInvocation *)invocation2 {3 }

13

Forwarding: Slow path

Pour que le runtime puisse créer son NSInvocation, il a besoin dela signature de la méthode.

On la demande gentiment à la classe du module que l’on est entrain d’usurper.

1 - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel2 {3 Method m = class_getInstanceMethod(_moduleClass, sel);4 if (m != nil) {5 const char *typeEncoging = method_getTypeEncoding(m);6 return [NSMethodSignature signatureWithObjCTypes:typeEncoging];7 }8 else {9 return nil;

10 }11 }

14

Comment mentir ?

Si l’on veux juste créer un module simple mais que l’on a besoinqu’ il réponde autre chose que 0 ou nil, comment peut-on faire?

15

1 @interface DZRGateKeeperModuleProxy : NSProxy2 - (id)initWithClass:(Class)moduleClass;3

4 /** The lying machine **/5 - (void)forwardSelector:(SEL)sel returningDefaultObject:(NSObject

*)object;♦

↣6 - (void)forwardSelector:(SEL)sel returningDefaultBOOL:(BOOL)boolean;7 - (void)forwardSelector:(SEL)sel returningDefaultFloat:(float)f;8 - (void)forwardSelector:(SEL)sel returningDefaultInteger:(NSInteger)i;9 - (void)forwardSelector:(SEL)sel returningDefaultDouble:(double)d;

10 @end

16

Implementation des stubs

1 - (void)forwardSelector:(SEL)sel returningDefaultObject:(NSObject*)object

♦↣

2 {3 _stubs[NSStringFromSelector(sel)] = object;4 }5

6 - (void)forwardSelector:(SEL)sel returningDefaultBOOL:(BOOL)boolean7 {8 _stubs[NSStringFromSelector(sel)] = @(boolean);9 }

17

Améliorons le forwardInfovation:

1 - (void)forwardInvocation:(NSInvocation *)invocation2 {3 id v;4 if ((v = [_stubs

objectForKey:NSStringFromSelector(invocation.selector)])) {♦

↣5 [invocation dzr_setReturnValue:v];6 }7 }

18

GateKeeper

Le chef d’orchestre

• Encore un singleton...• Les modules s’enregistrent auprès de lui• L’application l’appelle à des moments clef pourcharger/décharger des modules

19

Enregistrement d’un module

1 - (id)registerModule:(Class)module2 {3 DZRGateKeeperModule * moduleInstance = nil;4

5 if ([self checkDZRGateKeeperModule:module]) {6 NSString *name = [module name];7 DZRGateKeeperModuleProxy *proxy = [[DZRGateKeeperModuleProxy

alloc] initWithClass:module];♦

↣8 DZRGateKeeperModule * deactivatedInstance = moduleInstance =

[module deactivatedModule];♦

↣9 [deactivatedInstance moduleLoad];

10 proxy.module = deactivatedInstance;11 (self.modules)[name] = proxy;12 (self.registeredModules)[name] = module;13 }14 else {15 [NSException16 raise:NSGenericException format:@"You can only register class

deriving DZRGateKeeperModule"];♦

↣17 }18 return moduleInstance;19 }

20

Charger un module

1 - (DZRGateKeeperModule*)activateModuleWithName:(NSString *)moduleName2 {3 /** Check registration **/4

5 Class module = self.registeredModules[moduleName];6 DZRGateKeeperModuleProxy *proxy = self.modules[moduleName];7 DZRGateKeeperModule *moduleInstance = [module activatedModule];8

9 if (moduleInstance) {10 /** swap modules instances **/11 }12

13 return moduleInstance;14 }

21

Check registration

1 if (self.registeredModules[moduleName] == nil) {2 [NSException raise:NSGenericException3 format:@"The %@ module was not registered to the Gate

Keeper system",♦

↣4 moduleName];5 }

22

Swap modules instance

1 [proxy.module moduleUnload];2 [moduleInstance moduleLoad];3 proxy.module = moduleInstance;4 proxy.activated = YES;5 [[NSNotificationCenter defaultCenter]6 postNotificationName:DZRGateKeeperModuleLoadedNotification7 object:self8 userInfo:@{DZRGateKeeperUserInfoModuleInstance: proxy,9 DZRGateKeeperUserInfoModuleName: moduleName}];

23

Appliquer la configuration

1 - (void)applyConfiguration2 {3 /** Gathering necessary information **/4 /** Compute the set of module ids to desactivate **/5 /** Compute the set of module ids to activate **/6 [toDesactivate enumerateObjectsUsingBlock:^(NSString *module,

BOOL *stop) {♦

↣7 [self desactivateModuleWithName:module];8 }];9 [toActivate enumerateObjectsUsingBlock:^(NSString *module, BOOL

*stop) {♦

↣10 [self activateModuleWithName:module];11 }];12 }

24

Gathering necessary information

1 NSSet *activated = self.activatedModules;2 NSSet *configured = self.persistedConfiguration;3 NSSet *configuredAndForced = [configured

setByAddingObjectsFromSet:[DZRGateKeeper activationForced]];♦

↣4 NSSet *registered = [NSSet setWithArray:self.registeredModules.allKeys];

25

Compute the set of module ids to desactivate

1 NSMutableSet *toDesactivate = [activated mutableCopy];2 [toDesactivate minusSet:configuredAndForced];3 [toDesactivate intersectSet:registered];

26

Compute the set of module ids to activate

1 NSMutableSet *toActivate = [configuredAndForced mutableCopy];2 [toActivate minusSet:activated];3 [toActivate intersectSet:registered];

27

Conclusion

En résumé

Pro

• Rollout progressif et maitrisé

• Desactivation de fonctionalitéinstable

• Partagé avec les autresplateformes

Cons

• Impose une architecture clientpour les modules

• Mutiplication des configurations

• Difficile de supprimer un module(fonctionalité devenuepermanante)

28

Questions ?

28

top related