2017-09-20 5 views
1

J'essaie d'utiliser java 8 CompletionStages pour exécuter en série 2 méthodes asynchrones, afin que la seconde ne soit pas exécutée si la première échoue. Mais quand je l'appelle thenCompose, la fonction passée en semble commencer avant que la fonction précédente est terminée (par exemple:. Les deux fonctions exécuter par erreur en parallèle Voici le code:CompletionStage.thenCompose ne s'exécute pas en série

public CompletionStage<Graph> create(Payload payload) { 
    CompletionStage<BlobInfo> fileFuture = createFile(payload); 
    CompletionStage<Entity> metadataFuture = createMetadata(payload); 
    return fileFuture 
     .thenCompose(ignore -> metadataFuture) 
     .thenApply(entity -> 
      buildFromEntity(objectMapper, entity)); 
    } 

    public CompletionStage<BlobInfo> createFile(Payload payload) { 
    return CompletableFuture.supplyAsync(() -> { 
     try { 
     return 
      storage.create(
       BlobInfo 
        .newBuilder(payload.bucket, payload.name) 
        .build(), 
       payload.data.getBytes()); 
     } catch (StorageException e) { 
     LOG.error("Failed to write to storage: " + e); 
     throw new RequestHandlerException(StatusCode.SERVER_ERROR, 
      "Failed to write to storage."); 
     } 
    }); 
    } 


    public CompletionStage<Entity> createMetadata(Payload payload) { 
    return CompletableFuture.supplyAsync(() -> createSync(payload)); 
    } 

    private Entity createMetadataSync(Payload payload) { 
    Key key = keyFactory.newKey(payload.id); 
    Entity.Builder entityBuilder = GraphPayload.buildEntityFromGraph(payload, key); 
    Entity entity = entityBuilder.build(); 
    LOG.error("Metadata.createSync"); 

    try { 
     datastore.add(entity); 
    } catch (DatastoreException e) { 
     LOG.error("Failed to write initial metadata: " + e); 
     throw new RequestHandlerException(StatusCode.SERVER_ERROR, 
      "Failed to write initial metadata."); 
    } 
    return entity; 
    } 

SORTIE:

16:57:47.530 [ForkJoinPool.commonPool-worker-3] ERROR com.spotify.nfgraphstore.store.FileStore - CreateFile 
16:57:47.530 [ForkJoinPool.commonPool-worker-2] ERROR com.spotify.nfgraphstore.store.MetadataStore - Metadata.createSync 
16:57:47.530 [ForkJoinPool.commonPool-worker-3] ERROR com.spotify.nfgraphstore.store.FileStore - Failed to write initial graph to storage: com.google.cloud.storage.StorageException: X 

La sortie journalisée démontre que Metadata.createSync est exécutée avant que l'exception de stockage ne soit levée.Cette conclusion est également le résultat d'un test (non montré) censé afficher des interactions nulles avec la métadonnée DB si l'écriture dans le fichier la base de données de stockage échoue, ce test échoue parfois, ce qui suggère une condition de concurrence

Donc, je pense que thenCompose ne garantit pas l'exécution en série. Mais tout ce que j'ai lu dans les docs de java suggère que l'exécution devrait être en série: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html#thenCompose-java.util.function.Function-

Est-ce que quelqu'un sait pourquoi l'exécution n'est pas garantie pour être sérielle, ou recommande d'autres fonctions qui pourraient fonctionner plus comme je l'ai prévu?

Répondre

5

L'appel à createMetadata lance la tâche immédiatement, car il n'est pas appelé dans le cadre de l'expression lambda transmise à thenCompose.

Peut-être que vous vouliez faire:

.thenCompose(ignore -> createMetadata(payload)) 
+3

... ou tout simplement 'thenApplyAsync (ignorer -> createSync (charge utile))', car il n'y a aucune raison de conclure l'opération dans une autre 'CompletableFuture'. – Holger

+0

Merci. Cette réponse semble résoudre le problème. Je ne comprends pas pourquoi. J'ai ajouté d'autres instructions de débogage, et la ligne qui crée le futur des métadonnées ne provoque certainement pas l'invocation de createMetadataSync. Il doit donc être appelé immédiatement par thenCompose. Pourquoi thenCompose invoquer l'étape d'achèvement avant la fin de l'étape précédente? – jsarma

+0

Merci pour la réponse supplémentaire Holger. J'ai vérifié que cela fonctionne aussi. – jsarma