web-dev-qa-db-fra.com

"Le code Meteor doit toujours s'exécuter dans une fibre" lors de l'appel de Collection.insert sur le serveur

J'ai le code suivant dans server/statusboard.js;

var require = __meteor_bootstrap__.require,
    request = require("request")   


function getServices(services) {
  services = [];
  request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
    var resJSON = JSON.parse(body);
     _.each(resJSON, function(data) {
       var Host = data["Host_name"];
       var service = data["service_description"];
       var hardState = data["last_hard_state"];
       var currState = data["current_state"];
       services+={Host: Host, service: service, hardState: hardState, currState: currState};
       Services.insert({Host: Host, service: service, hardState: hardState, currState: currState});
    });
  });
}

Meteor.startup(function () {
  var services = [];
  getServices(services);
  console.log(services);
});

Fondamentalement, il extrait certaines données d'un flux JSON et essaie de les pousser dans une collection.

Lorsque je démarre Meteor, je reçois l'exception suivante;

app/packages/livedata/livedata_server.js:781
      throw exception;
            ^
Error: Meteor code must always run within a Fiber
    at [object Object].withValue (app/packages/meteor/dynamics_nodejs.js:22:15)
    at [object Object].apply (app/packages/livedata/livedata_server.js:767:45)
    at [object Object].insert (app/packages/mongo-livedata/collection.js:199:21)
    at app/server/statusboard.js:15:16
    at Array.forEach (native)
    at Function.<anonymous> (app/packages/underscore/underscore.js:76:11)
    at Request._callback (app/server/statusboard.js:9:7)
    at Request.callback (/usr/local/meteor/lib/node_modules/request/main.js:108:22)
    at Request.<anonymous> (/usr/local/meteor/lib/node_modules/request/main.js:468:18)
    at Request.emit (events.js:67:17)
Exited with code: 1

Je ne sais pas trop ce que signifie cette erreur. Quelqu'un a-t-il des idées ou peut-il suggérer une approche différente?

38
Andrew Beresford

Comme mentionné ci-dessus, c'est parce que votre code d'exécution dans un rappel.

Tout code que vous exécutez côté serveur doit être contenu dans une fibre.

Essayez de changer votre fonction getServices pour qu'elle ressemble à ceci:

function getServices(services) {
  Fiber(function() { 
    services = [];
    request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
      var resJSON = JSON.parse(body);
       _.each(resJSON, function(data) {
         var Host = data["Host_name"];
         var service = data["service_description"];
         var hardState = data["last_hard_state"];
         var currState = data["current_state"];
         services+={Host: Host, service: service, hardState: hardState, currState: currState};
         Services.insert({Host: Host, service: service, hardState: hardState, currState: currState});
      });
    });
  }).run();  
}

Je viens de rencontrer un problème similaire et cela a fonctionné pour moi. Ce que je dois dire cependant, c'est que je suis très nouveau dans ce domaine et je ne sais pas si c'est ainsi que cela doit être fait.

Vous pourriez probablement vous en sortir en enveloppant uniquement votre instruction d'insertion dans la fibre, mais je ne suis pas positif.

15
Zeman4323

Le simple fait d'envelopper votre fonction dans une fibre peut ne pas suffire et peut conduire à un comportement inattendu.

La raison est que, avec Fibre, Meteor nécessite un ensemble de variables attachées à une fibre. Meteor utilise les données attachées à une fibre comme une étendue dynamique et la façon la plus simple de l'utiliser avec une API tierce est d'utiliser Meteor.bindEnvironment.

T.post('someurl', Meteor.bindEnvironment(function (err, res) {
  // do stuff
  // can access Meteor.userId
  // still have MongoDB write fence
}, function () { console.log('Failed to bind environment'); }));

Regardez ces vidéos sur l'esprit des événements si vous voulez en savoir plus: https://www.eventedmind.com/posts/meteor-dynamic-scoping-with-environment-variableshttps://www.eventedmind.com/posts/meteor-what-is-meteor-bindenvironment

48
imslavko

Sur la base de mes tests, vous devez envelopper l'insert dans du code que j'ai testé qui est similaire à l'exemple ci-dessus.

Par exemple, je l'ai fait et cela a toujours échoué avec une erreur de fibres.

function insertPost(args) {
  if(args) {
Fiber(function() { 
    post_text = args.text.slice(0,140);
    T.post('statuses/update', { status: post_text }, 
        function(err, reply) {          
            if(reply){
                // TODO remove console output
                console.log('reply: ' + JSON.stringify(reply,0,4));
                console.log('incoming Twitter string: ' + reply.id_str);
                // TODO insert record
                var ts = Date.now();
                id = Posts.insert({
                    post: post_text, 
                    Twitter_id_str: reply.id_str,
                    created: ts
                });
            }else {
                console.log('error: ' + JSON.stringify(err,0,4));
                // TODO maybe store locally even though it failed on Twitter
                // and run service in background to Push them later?
            }
        }
    );
}).run();
  }
}

Je l'ai fait et cela s'est bien passé sans erreurs.

function insertPost(args) {
  if(args) { 
post_text = args.text.slice(0,140);
T.post('statuses/update', { status: post_text }, 
    function(err, reply) {          
        if(reply){
            // TODO remove console output
            console.log('reply: ' + JSON.stringify(reply,0,4));
            console.log('incoming Twitter string: ' + reply.id_str);
            // TODO insert record
            var ts = Date.now();
            Fiber(function() {
                id = Posts.insert({
                    post: post_text, 
                    Twitter_id_str: reply.id_str,
                    created: ts
                });
            }).run();
        }else {
            console.log('error: ' + JSON.stringify(err,0,4));
            // TODO maybe store locally even though it failed on Twitter
            // and run service in background to Push them later?
        }
    }
);
  }
}

J'ai pensé que cela pourrait aider d'autres personnes à rencontrer ce problème. Je n'ai pas encore testé d'appeler le type de service externe asynchrone après le code interne et de l'envelopper dans une fibre. Cela pourrait également valoir la peine d'être testé. Dans mon cas, j'avais besoin de savoir que l'action à distance s'était produite avant de faire mon action locale.

J'espère que cela contribue à ce fil de questions.

7
Steeve Cannon