Daemons ne lit pas toutes les données

36 views
Skip to first unread message

ziburudebian

unread,
Aug 31, 2016, 10:06:44 AM8/31/16
to Railsfrance
Bonjour à tous

J'utilise la gem daemon mais mon programme ne lit pas toutes les données de ma table client : il y a 5 enregistrements, il s'arrête au deuxième !!! ça fait trois jours que je cherche, j'en peux plus ....
Voici mon code :

runexecution est la fonction que je lance depuis le bouton de mon formulaire; elle est décrite dans app/controllers/executions_controller.rb

  class ExecutionsController < ApplicationController
  
  def runexecution
     # ============================================================================================================
     # DESCRIPTION : Permet de demarrer ou arreter l'execution d'un script (action=start ou stop)
     # ============================================================================================================
     require 'daemons'      
     # ----------------------------
     # traitement des parametres
     # ----------------------------    
     return "" if params.nil? # ce test  permet de ne pas rentrer dans la fonction lors de l'appel via le menu
     action = ""
     params.each do |key,value| 
        action = "start" if (key.index("loopstart") != nil)
        action = "stop" if (key.index("loopstop") != nil)  
     end 
     logger.info("******************action="+action)       
     case action
     when "start"
        exedate = "2015-10-03 08:30"
        frequence = "0.05"
listclientselectionne = "1-2-3-4-5"
        paramopt = ""
        options = {:ARGV       => [action, paramopt, '--', exedate, frequence, listclientselectionne],
                   :dir_mode   => :script,
                   :dir        => 'tmp/pids',
                   :multiple   => true,
                   :ontop      => true,
                   :mode       => :load,
                   :backtrace  => true,
                   :monitor    => true,
                   :log_output => true
                  }
        logger.info("******************options="+options.to_s) 
        Daemons.run('myserver.rb', options)
        logger.info("******************FIN") 
        
     when "stop"
        logger.info("******************loopstop")            
     else
       redirect_to executions_execution_path
     end # case action
     
  end # runexecution
  
  end #class

le programme qui tourne en boucle est myserver.rb qui est à la racine de mon site :

#!/usr/bin/env ruby 
ENV['RAILS_ENV'] ||= 'production'
require File.expand_path('../config/environment',  __FILE__)
print "\nParametres= " + ARGV[0].to_s + "****" + ARGV[1] + "****" + ARGV[2] + "\n"
exedate = ARGV[0] 
frequence = ARGV[1] 
listclientselectionne = ARGV[2]  
delai = frequence.to_f * 60   
listclientselectionne = listclientselectionne.split("-")
print "Nbclient=" + listclientselectionne.count.to_s + "\n"
loop do
  print Time.now.to_s + "\n"
  listclientselectionne.each do |idcl|
    print idcl + "\n"
    begin
      print "avant\n"
      objclient = Client.find(idcl)
      print "apres\n"
      if (!objclient.nil?)
        print objclient.lastname.to_s + "\n"
        # print objclient.name + "\n"
      else
        print "Erreur sur " + idcl + "\n"
      end
    rescue  ActiveRecord::RecordNotFound => e
      objclient = nil
      print "Erreur \n"
    end
  end
  sleep(delai)
end

et voici les résultat à 'écran

=> Booting Thin
=> Rails 4.1.7 application starting in production on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
Thin web server (v1.6.4 codename Gob Bluth)
Maximum connections set to 1024
Listening on 0.0.0.0:3000, CTRL+C to stop
myserver.rb: process with pid 28318 started.

Parametres= 2015-10-03 08:30****0.05****5-1-6-2
Nbclient=5
2016-08-31 15:47:56 +0200
1
avant
apres
DURANTON
2
avant
apres
VALENT
3
avant
log writing failed. closed stream
log writing failed. closed stream
log writing failed. closed stream


Si j'exécute le programme myserver.rb depuis la console avec ruby myserver.rb 1 2 3 : tout se passe bien; voici le stdout

# ruby myserver.rb 1 2 3
Parametres= 1****2****3
Nbclient=5
2016-08-31 16:04:10 +0200
1
avant
apres
DURANTON888
2
avant
apres
VALENT
3
avant
apres
VIGILE1AA
4
avant
apres
VIGILE2
5
avant
apres
DURANDTON



Si vous avez une idée

Merci beaucoup

Ma config
  • Debian GNU/Linux 7
  • Rails 4.17
  • Ruby 1.91

Philippe Creux

unread,
Sep 1, 2016, 5:53:08 AM9/1/16
to rails...@googlegroups.com
Bonjour,

Rails et daemon ne doivent pas faire bon ménage.

Pour faire tourner des tâches de manière asynchrone dans une application Ruby on Rails, je te conseille d'utiliser ActiveJob (http://guides.rubyonrails.org/active_job_basics.html) et Sidekiq.

Bon courage!

-- 
φ


--
--
Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance" de Google Groups.
Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse rails...@googlegroups.com
Pour résilier votre abonnement envoyez un e-mail à l'adresse railsfrance-unsubscribe@googlegroups.com
---
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Railsfrance".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse railsfrance+unsubscribe@googlegroups.com.
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.

Florian Dutey

unread,
Sep 1, 2016, 1:14:26 PM9/1/16
to rails...@googlegroups.com
Si tu veux utiliser daemon et rails en meme temps, il faut que tu ecrives dans une queue intermediaire (par exemple redis). Le daemon monitor la queue (boucle infinie), quand ya des nouveaux messages dedans, il les depiles et les traite. Ton controller ne doit jamais inclure la gem daemon et tu ne dois jamais appeler des scrips depuis ton controller, sinon la gem daemon ne sert a rien en fait, tu peux appeler le script directement avec system() et c'est degueulasse.

Quand a ton probleme courant, ce qu'on voit dans ton exemple, c'est que tu passes tes ids client en mode "1-2-3-4" depuis le controller et en mode "1 2 3" depuis le shell.

Quand au script lui meme, pourquoi lui filer une loop alors que ca sert a rien? C'est pas daemon, c'est un script shell ca
C'est aussi plus clean de faire un truc genre Client.find(*client_ids).each { } plutot que client_ids.each { |id| c = Client.find(id) #... } Dans un cas t'as un seul call SQL et dans l'autre, t'as autant de calls que d'ids.

Le code de ton daemon est tres confus. On ne demarre pas un daemon depuis un controller. Le daemon doit tourner tout le temps et recevoir des signaux. La tu ne controlles pas combien de daemon seront demarres en parallele. Il suffit de spammer ton serveur pour demarrer des tonnes de daemon et faire peter l'OS qui sera perdu dans les millions de process crees par le serveur. Le fait de stopper quand ton utilisateur veut bien penser a cliquer sur stop (ce qu'il ne fera jamais) me permet de te predire que tu vas devoir redemarrer tes serveurs souvent, et que ton app ne serait jamais disponible.

En fait, si tu decrivais ce que tu cherches reellement a faire, ca permettrait probablement de t'aiguiller un peu mieux. Mais la, le code, y'a tellement de fondamentaux qui manquent que personne peut prendre le temps de te donner la solution.

ziburudebian

unread,
Sep 2, 2016, 5:35:14 AM9/2/16
to Railsfrance
Merci florian pour tes explications, c'est vrai que fournir du code ainsi peut paraître complexe, mais au bout d'un moment tout seul dans mon coin à coder, je craque...
tu as raison, j'aurai du commencer par expliquer ce que je veux faire : 

sur mon formulaire j'ai 2 boutons START et STOP comme tu l'as compris dans mon code; 
le but lorsque je clique sur START est de lancer un programme qui à partir d'options choisies sur ma page web, analyse les données dans la base et affiche le résultat sur ma page web et le STOP arrête cette analyse; 
les données (que j'analyse) qui alimente ma base sont mises à jour par un programme externe toutes les 10 minutes, c'est pour cela que j'ai pensé à un boucle infinie qui démarre sur le bouton START

-- 
φ


Pour résilier votre abonnement envoyez un e-mail à l'adresse railsfrance...@googlegroups.com

---
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Railsfrance".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse railsfrance...@googlegroups.com.

Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.

--
--
Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance" de Google Groups.
Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse rails...@googlegroups.com
Pour résilier votre abonnement envoyez un e-mail à l'adresse railsfrance...@googlegroups.com

---
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Railsfrance".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse railsfrance...@googlegroups.com.

Olivier El Mekki

unread,
Sep 2, 2016, 6:14:51 AM9/2/16
to Railsfrance
Hello,

Dans le cas que tu décris, je partirais sur quelque chose de plus simple que d'utiliser daemon (qui n'est probablement pas pensé pour être utilisé avec rails): lancer ton app en commande shell, lui faire stocker son pid dans un pidfile, puis faire un kill sur le contenu de ce fichier. Voici un example:

La partie web:

```
# Ce fichier est en plain ruby, mais l'idée est de reproduire le contenu
# des methodes #start et #stop dans tes controllers
def start
  process = spawn( "ruby ./app.rb" ) # tu lances ton process
  Process.detach( process ) # tu le détaches de ton process web
end

def stop
  %x(kill $(cat ./app.pid)) if File.exists? "./app.pid"  # tu killes ton process, s'il n'est pas déjà mort
end

puts "starting"
start
sleep 5
puts "stopping"
stop
```

Dans ton cas, tu voudras probablement utiliser des path absolus plutôt que relatifs.

La partie system:

```
# tu stock le pid de ton process
File.open( "./app.pid", "w" ){ |f| f.puts Process.pid }

# tu t'assures de supprimer le pidfile quand ton process meurt
Kernel.at_exit do
  File.unlink( "./app.pid" )
end


# Code

sleep 50 # juste pour executer cet example, tu n'en as pas besoin dans ton code
```

Florian Dutey

unread,
Sep 2, 2016, 2:27:21 PM9/2/16
to rails...@googlegroups.com
Je pense tres honnetement que ce procede est mauvais. Tu controlles pas qui lance (et combien) de process. C'est un coup a te retrouver avec plein de process orphelins et a obtenir un deni de service de la part de ton OS.

Le cas d'utilisation est plutot simple ici. T'as une base de donnees qui est updatee regulierement.
Dans ce cas la, tu as 2 options tres simples:

1) Tu fais du polling. Toutes les 10 minutes, ton frontend (javascript) fait une requete (ajax) au backend (rails) pour recuperer le nouveau data. 
2) tu utilises des websockets (action cable avec rails 5). C'est le serveur qui va pousser les donnees vers le client chaque fois qu'il y en a des nouvelles. Donc ca evite de faire des requetes en boucle vers le serveur.

Quand au boutton start stop, dans le premier cas, start demarre le cycle de requetes ajax toutes les 10 minutes (avec setTimeout en vanilla JS). Et le boutton stop detruit cette boucle (clearTimeout en vanilla JS).

Dans le second cas, les bouttons start et stop mettent juste a jour un boolean dans les etats de ta vue. Boolean qui sera evalue pour savoir si tu dois ignorer ou non les evenements qui sont pousses par le serveur.

Pas besoin de jobs, pas besoin de daemon, pas besoin de boucle infinie cote serveur dans ce cas la. C'est un cas tres classique d'affichage de data "en temps reel".

Pour résilier votre abonnement envoyez un e-mail à l'adresse railsfrance-unsubscribe@googlegroups.com

---
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Railsfrance".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse railsfrance+unsubscribe@googlegroups.com.

Olivier El Mekki

unread,
Sep 2, 2016, 4:31:06 PM9/2/16
to Railsfrance
Nous parlons à quelqu'un qui débute manifestement, et il n'a pas été fait mention que l'application serait publique. Sous réserve de confirmation par Ziburudebian, j'ai l'impression qu'il essaie de se bidouiller un outil d'administration à usage personnel, voir local. Dans tous les cas, débuter rails et devoir commencer par sidekiq, action_cable, une configuration de redis, etc est un tue-l'amour pour les débutants. Clairement, nous ne ferions pas de la gestion de process à l'arrache comme ça dans des applications at scale destinées au public. Mais est-ce bien la question ici?

Tu as raison cependant, ça ne ferait pas de mal de faire un check de l'existence d'un process en cours. Étant donné qu'il y a un pidfile, c'est assez simple ça faire:

```
def start
  raise "Process is already running" if File.exists? "./app.pid"
  process = spawn( "ruby ./app.rb" ) # tu lances ton process
  Process.detach( process ) # tu le détaches de ton process web
end
```

En cas de coupure brutale du système, il suffit de supprimer le pidfile (c'est assez courant avec les architectures à base de lockfile).

Cette solution, aussi peu élégante soit elle, à l'avantage d'être abordable pour un débutant qui essaie d'accomplir quelque chose à son usage. Les architectures plus complexes viendront avec l'envie et l'expérience.

Ziburudebian, à bon entendeur : tu peux faire quelque chose aussi simple à ton usage personnel pour débuter, mais c'est un peu plus complexe que ça si tu veux faire une application business / publique :)

ziburudebian

unread,
Sep 4, 2016, 4:44:47 AM9/4/16
to Railsfrance
Merci florian ta proposition me plait et je vais la mettre en oeuvre

ziburudebian

unread,
Sep 4, 2016, 4:47:35 AM9/4/16
to Railsfrance
merci olivier pour ta solution mais je préfère partir sur celle de florian; désolé de te décevoir, mais j'ai plus d'un an et demi d'expérience en RubyOnRails (tu dois me trouver vraiment nul au travers de mon code...) et application web; de plus ma solution est professionnelle et sensible; donc je préfère partir sur qq chose de plus complexe à mettre en oeuvre mais de plus costaud

Merci encore

ZinburuDebain

Olivier El Mekki

unread,
Sep 4, 2016, 7:59:56 AM9/4/16
to Railsfrance
Au temps pour moi, et désolé :) Si tu veux faire un usage professionnel de ce code, il faut effectivement que tu partes sur une des pistes avancées par Florian.

En toute franchise, en voyant le code que tu as initialement posté, je pense que tu n'es pas prêt pour mettre en production des applications qui sous-tendent un business. Je sais que c'est désagréable à entendre, mais prendre un peu plus de temps à te former t'évitera de découvrir qu'un petit problème de code peut devenir un énorme problème lorsqu'il y a de l'argent derrière.

Voici ce que je te conseille:

* lis des livres populaires et récents sur le sujet de rails
* lis autant de code opensource que possible (c'est souvent gros, mais parfois il suffit de lire un fichier sans saisir toute l'architecture pour saisir un pattern)
* multiplie les petits projets avant de te lancer dans un gros, pendant que tu avances sur le reste. Ça t'entrainera et permettra de mesurer ta progression
* participe à des ateliers, comme ceux de humancoders, ou ceux organisés autour des conférences

En espérant ne pas trop être un downer,
Reply all
Reply to author
Forward
0 new messages