Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TQueryResultManager.cxx
Go to the documentation of this file.
1 // @(#)root/proof:$Id$
2 // Author: G. Ganis Mar 2008
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TQueryResultManager
13 \ingroup proofkernel
14 
15 Class managing the query-result area
16 
17 */
18 
19 #include <errno.h>
20 #ifdef WIN32
21 # include <io.h>
22 #endif
23 
24 #include "TQueryResultManager.h"
25 
26 #include "TFile.h"
27 #include "THashList.h"
28 #include "TKey.h"
29 #include "TProofQueryResult.h"
30 #include "TObjString.h"
31 #include "TParameter.h"
32 #include "TProof.h"
33 #include "TProofServ.h"
34 #include "TRegexp.h"
35 #include "TSortedList.h"
36 #include "TSystem.h"
37 #include "TVirtualProofPlayer.h"
38 
39 ////////////////////////////////////////////////////////////////////////////////
40 /// Constructor
41 
42 TQueryResultManager::TQueryResultManager(const char *qdir, const char *stag,
43  const char *sdir,
44  TProofLockPath *lck, FILE *logfile)
45 {
46  fQueryDir = qdir;
47  fSessionTag = stag;
48  fSessionDir = sdir;
49  fSeqNum = 0;
50  fDrawQueries = 0;
51  fKeptQueries = 0;
52  fQueries = new TList;
53  fPreviousQueries = 0;
54  fLock = lck;
55  fLogFile = (logfile) ? logfile : stdout;
56 }
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 /// Cleanup. Not really necessary since after this dtor there is no
60 /// live anyway.
61 
62 TQueryResultManager::~TQueryResultManager()
63 {
64  SafeDelete(fQueries);
65  SafeDelete(fPreviousQueries);
66 }
67 
68 ////////////////////////////////////////////////////////////////////////////////
69 /// Add part of log file concerning TQueryResult pq to its macro
70 /// container.
71 
72 void TQueryResultManager::AddLogFile(TProofQueryResult *pq)
73 {
74  if (!pq)
75  return;
76 
77  // Make sure everything is written to file
78  fflush(fLogFile);
79 
80  // Save current position
81  off_t lnow = 0;
82  if ((lnow = lseek(fileno(fLogFile), (off_t) 0, SEEK_CUR)) < 0) {
83  Error("AddLogFile", "problems lseeking current position on log file (errno: %d)", errno);
84  return;
85  }
86 
87  // The range we are interested in
88  Int_t start = pq->fStartLog;
89  if (start > -1)
90  lseek(fileno(fLogFile), (off_t) start, SEEK_SET);
91 
92  // Read the lines and add then to the internal container
93  const Int_t kMAXBUF = 4096;
94  char line[kMAXBUF];
95  while (fgets(line, sizeof(line), fLogFile)) {
96  if (line[strlen(line)-1] == '\n')
97  line[strlen(line)-1] = 0;
98  pq->AddLogLine((const char *)line);
99  }
100 
101  // Restore initial position if partial send
102  if (lnow >= 0) lseek(fileno(fLogFile), lnow, SEEK_SET);
103 }
104 ////////////////////////////////////////////////////////////////////////////////
105 /// Remove all queries results referring to previous sessions
106 
107 Int_t TQueryResultManager::CleanupQueriesDir()
108 {
109  Int_t nd = 0;
110 
111  // Cleanup previous stuff
112  if (fPreviousQueries) {
113  fPreviousQueries->Delete();
114  SafeDelete(fPreviousQueries);
115  }
116 
117  // Loop over session dirs
118  TString queriesdir = fQueryDir;
119  queriesdir = queriesdir.Remove(queriesdir.Index(kPROOF_QueryDir) +
120  strlen(kPROOF_QueryDir));
121  void *dirs = gSystem->OpenDirectory(queriesdir);
122  if (dirs) {
123  char *sess = 0;
124  while ((sess = (char *) gSystem->GetDirEntry(dirs))) {
125 
126  // We are interested only in "session-..." subdirs
127  if (strlen(sess) < 7 || strncmp(sess,"session",7))
128  continue;
129 
130  // We do not want this session at this level
131  if (strstr(sess, fSessionTag))
132  continue;
133 
134  // Remove the directory
135  TString qdir;
136  qdir.Form("%s/%s", queriesdir.Data(), sess);
137  PDB(kGlobal, 1)
138  Info("RemoveQuery", "removing directory: %s", qdir.Data());
139  gSystem->Exec(Form("%s %s", kRM, qdir.Data()));
140  nd++;
141  }
142  // Close directory
143  gSystem->FreeDirectory(dirs);
144  } else {
145  Warning("RemoveQuery", "cannot open queries directory: %s", queriesdir.Data());
146  }
147 
148  // Done
149  return nd;
150 }
151 
152 ////////////////////////////////////////////////////////////////////////////////
153 /// Scan the queries directory for the results of previous queries.
154 /// The headers of the query results found are loaded in fPreviousQueries.
155 /// The full query result can be retrieved via TProof::Retrieve.
156 
157 void TQueryResultManager::ScanPreviousQueries(const char *dir)
158 {
159  // Cleanup previous stuff
160  if (fPreviousQueries) {
161  fPreviousQueries->Delete();
162  SafeDelete(fPreviousQueries);
163  }
164 
165  // Loop over session dirs
166  void *dirs = gSystem->OpenDirectory(dir);
167  char *sess = 0;
168  while ((sess = (char *) gSystem->GetDirEntry(dirs))) {
169 
170  // We are interested only in "session-..." subdirs
171  if (strlen(sess) < 7 || strncmp(sess,"session",7))
172  continue;
173 
174  // We do not want this session at this level
175  if (strstr(sess, fSessionTag))
176  continue;
177 
178  // Loop over query dirs
179  void *dirq = gSystem->OpenDirectory(Form("%s/%s", dir, sess));
180  char *qry = 0;
181  while ((qry = (char *) gSystem->GetDirEntry(dirq))) {
182 
183  // We are interested only in "n/" subdirs
184  if (qry[0] == '.')
185  continue;
186 
187  // File with the query result
188  TString fn = Form("%s/%s/%s/query-result.root", dir, sess, qry);
189  TFile *f = TFile::Open(fn);
190  if (f) {
191  f->ReadKeys();
192  TIter nxk(f->GetListOfKeys());
193  TKey *k = 0;
194  TProofQueryResult *pqr = 0;
195  while ((k = (TKey *)nxk())) {
196  if (!strcmp(k->GetClassName(), "TProofQueryResult")) {
197  pqr = (TProofQueryResult *) f->Get(k->GetName());
198  if (pqr) {
199  TQueryResult *qr = pqr->CloneInfo();
200  if (qr) {
201  if (!fPreviousQueries)
202  fPreviousQueries = new TList;
203  if (qr->GetStatus() > TQueryResult::kRunning) {
204  fPreviousQueries->Add(qr);
205  } else {
206  // (For the time being) remove a non completed
207  // query if not owned by anybody
208  TProofLockPath *lck = 0;
209  if (LockSession(qr->GetTitle(), &lck) == 0) {
210  RemoveQuery(qr);
211  // Unlock and remove the lock file
212  SafeDelete(lck);
213  }
214  }
215  } else {
216  Warning("ScanPreviousQueries", "unable to clone TProofQueryResult '%s:%s'",
217  pqr->GetName(), pqr->GetTitle());
218  }
219  }
220  }
221  }
222  f->Close();
223  delete f;
224  }
225  }
226  gSystem->FreeDirectory(dirq);
227  }
228  gSystem->FreeDirectory(dirs);
229 }
230 
231 ////////////////////////////////////////////////////////////////////////////////
232 /// Scan the queries directory and remove the oldest ones (and relative dirs,
233 /// if empty) in such a way only 'mxq' queries are kept.
234 /// Return 0 on success, -1 in case of problems
235 
236 Int_t TQueryResultManager::ApplyMaxQueries(Int_t mxq)
237 {
238  // Nothing to do if mxq is -1.
239  if (mxq < 0)
240  return 0;
241 
242  // We will sort the entries using the creation time
243  TSortedList *sl = new TSortedList;
244  sl->SetOwner();
245  // List with information
246  THashList *hl = new THashList;
247  hl->SetOwner();
248 
249  // Keep track of the queries per session dir
250  TList *dl = new TList;
251  dl->SetOwner();
252 
253  // Loop over session dirs
254  TString dir = fQueryDir;
255  Int_t idx = dir.Index("session-");
256  if (idx != kNPOS)
257  dir.Remove(idx);
258  void *dirs = gSystem->OpenDirectory(dir);
259  char *sess = 0;
260  while ((sess = (char *) gSystem->GetDirEntry(dirs))) {
261 
262  // We are interested only in "session-..." subdirs
263  if (strlen(sess) < 7 || strncmp(sess,"session",7))
264  continue;
265 
266  // We do not want this session at this level
267  if (strstr(sess, fSessionTag))
268  continue;
269 
270  // Loop over query dirs
271  Int_t nq = 0;
272  void *dirq = gSystem->OpenDirectory(Form("%s/%s", dir.Data(), sess));
273  char *qry = 0;
274  while ((qry = (char *) gSystem->GetDirEntry(dirq))) {
275 
276  // We are interested only in "n/" subdirs
277  if (qry[0] == '.')
278  continue;
279 
280  // File with the query result
281  TString fn = Form("%s/%s/%s/query-result.root", dir.Data(), sess, qry);
282 
283  FileStat_t st;
284  if (gSystem->GetPathInfo(fn, st)) {
285  PDB(kGlobal, 1)
286  Info("ApplyMaxQueries","file '%s' cannot be stated: remove it", fn.Data());
287  gSystem->Unlink(gSystem->DirName(fn));
288  continue;
289  }
290 
291  // Add the entry in the sorted list
292  sl->Add(new TObjString(TString::Format("%ld", st.fMtime)));
293  hl->Add(new TNamed((const char*)TString::Format("%ld",st.fMtime), fn.Data()));
294  nq++;
295  }
296  gSystem->FreeDirectory(dirq);
297 
298  if (nq > 0)
299  dl->Add(new TParameter<Int_t>(TString::Format("%s/%s", dir.Data(), sess), nq));
300  else
301  // Remove it
302  gSystem->Exec(TString::Format("%s -fr %s/%s", kRM, dir.Data(), sess));
303  }
304  gSystem->FreeDirectory(dirs);
305 
306  // Now we apply the quota
307  TIter nxq(sl, kIterBackward);
308  Int_t nqkept = 0;
309  TObjString *os = 0;
310  while ((os = (TObjString *)nxq())) {
311  if (nqkept < mxq) {
312  // Keep this and go to the next
313  nqkept++;
314  } else {
315  // Clean this
316  TNamed *nm = dynamic_cast<TNamed *>(hl->FindObject(os->GetName()));
317  if (nm) {
318  gSystem->Unlink(nm->GetTitle());
319  // Update dir counters
320  TString tdir(gSystem->DirName(nm->GetTitle()));
321  tdir = gSystem->DirName(tdir.Data());
322  TParameter<Int_t> *nq = dynamic_cast<TParameter<Int_t>*>(dl->FindObject(tdir));
323  if (nq) {
324  Int_t val = nq->GetVal();
325  nq->SetVal(--val);
326  if (nq->GetVal() <= 0)
327  // Remove the directory if empty
328  gSystem->Exec(Form("%s -fr %s", kRM, tdir.Data()));
329  }
330  }
331  }
332  }
333 
334  // Cleanup
335  delete sl;
336  delete hl;
337  delete dl;
338 
339  // Done
340  return 0;
341 }
342 
343 ////////////////////////////////////////////////////////////////////////////////
344 /// Try locking query area of session tagged sessiontag.
345 /// The id of the locking file is returned in fid and must be
346 /// unlocked via UnlockQueryFile(fid).
347 
348 Int_t TQueryResultManager::LockSession(const char *sessiontag, TProofLockPath **lck)
349 {
350  // We do not need to lock our own session
351  if (strstr(sessiontag, fSessionTag))
352  return 0;
353 
354  if (!lck) {
355  Error("LockSession","locker space undefined");
356  return -1;
357  }
358  *lck = 0;
359 
360  // Check the format
361  TString stag = sessiontag;
362  TRegexp re("session-.*-.*-.*-.*");
363  Int_t i1 = stag.Index(re);
364  if (i1 == kNPOS) {
365  Error("LockSession","bad format: %s", sessiontag);
366  return -1;
367  }
368  stag.ReplaceAll("session-","");
369 
370  // Drop query number, if any
371  Int_t i2 = stag.Index(":q");
372  if (i2 != kNPOS)
373  stag.Remove(i2);
374 
375  // Make sure that parent process does not exist anylonger
376  TString parlog = fSessionDir;
377  parlog = parlog.Remove(parlog.Index("master-")+strlen("master-"));
378  parlog += stag;
379  if (!gSystem->AccessPathName(parlog)) {
380  PDB(kGlobal, 1)
381  Info("LockSession", "parent still running: do nothing");
382  return -1;
383  }
384 
385  // Lock the query lock file
386  if (fLock) {
387  TString qlock = fLock->GetName();
388  qlock.ReplaceAll(fSessionTag, stag);
389 
390  if (!gSystem->AccessPathName(qlock)) {
391  *lck = new TProofLockPath(qlock);
392  if (((*lck)->Lock()) < 0) {
393  Error("LockSession","problems locking query lock file");
394  SafeDelete(*lck);
395  return -1;
396  }
397  }
398  }
399 
400  // We are done
401  return 0;
402 }
403 
404 ////////////////////////////////////////////////////////////////////////////////
405 /// Cleanup query dir qdir.
406 
407 Int_t TQueryResultManager::CleanupSession(const char *sessiontag)
408 {
409  if (!sessiontag) {
410  Error("CleanupSession","session tag undefined");
411  return -1;
412  }
413 
414  // Query dir
415  TString qdir = fQueryDir;
416  qdir.ReplaceAll(Form("session-%s", fSessionTag.Data()), sessiontag);
417  Int_t idx = qdir.Index(":q");
418  if (idx != kNPOS)
419  qdir.Remove(idx);
420  if (gSystem->AccessPathName(qdir)) {
421  Info("CleanupSession","query dir %s does not exist", qdir.Data());
422  return -1;
423  }
424 
425  TProofLockPath *lck = 0;
426  if (LockSession(sessiontag, &lck) == 0) {
427 
428  // Cleanup now
429  gSystem->Exec(Form("%s %s", kRM, qdir.Data()));
430 
431  // Unlock and remove the lock file
432  if (lck) {
433  gSystem->Unlink(lck->GetName());
434  SafeDelete(lck); // Unlocks, if necessary
435  }
436 
437  // We are done
438  return 0;
439  }
440 
441  // Notify failure
442  Info("CleanupSession", "could not lock session %s", sessiontag);
443  return -1;
444 }
445 
446 ////////////////////////////////////////////////////////////////////////////////
447 /// Save current status of query 'qr' to file name fout.
448 /// If fout == 0 (default) use the default name.
449 
450 void TQueryResultManager::SaveQuery(TProofQueryResult *qr, const char *fout)
451 {
452  if (!qr || qr->IsDraw())
453  return;
454 
455  // Create dir for specific query
456  TString querydir = Form("%s/%d",fQueryDir.Data(), qr->GetSeqNum());
457 
458  // Create dir, if needed
459  if (gSystem->AccessPathName(querydir))
460  gSystem->MakeDirectory(querydir);
461  TString ofn = fout ? fout : Form("%s/query-result.root", querydir.Data());
462 
463  // Recreate file and save query in its current status
464  TFile *f = TFile::Open(ofn, "RECREATE");
465  if (f) {
466  f->cd();
467  if (!(qr->IsArchived()))
468  qr->SetResultFile(ofn);
469  qr->Write();
470  f->Close();
471  delete f;
472  }
473 }
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 /// Remove everything about query queryref; if defined 'otherlist' will containe
477 /// the list of removed pointers (already deleted)
478 
479 void TQueryResultManager::RemoveQuery(const char *queryref, TList *otherlist)
480 {
481  PDB(kGlobal, 1)
482  Info("RemoveQuery", "Enter");
483 
484  // Parse reference string
485  Int_t qry = -1;
486  TString qdir;
487  TProofQueryResult *pqr = LocateQuery(queryref, qry, qdir);
488  // Remove instance in memory
489  if (pqr) {
490  if (qry > -1) {
491  fQueries->Remove(pqr);
492  if (otherlist) otherlist->Add(pqr);
493  } else
494  fPreviousQueries->Remove(pqr);
495  delete pqr;
496  pqr = 0;
497  }
498 
499  // Remove the directory
500  PDB(kGlobal, 1)
501  Info("RemoveQuery", "removing directory: %s", qdir.Data());
502  gSystem->Exec(Form("%s %s", kRM, qdir.Data()));
503 
504  // Done
505  return;
506 }
507 
508 ////////////////////////////////////////////////////////////////////////////////
509 /// Remove everything about query qr. If soft = TRUE leave a track
510 /// in memory with the relevant info
511 
512 void TQueryResultManager::RemoveQuery(TQueryResult *qr, Bool_t soft)
513 {
514  PDB(kGlobal, 1)
515  Info("RemoveQuery", "Enter");
516 
517  if (!qr)
518  return;
519 
520  // Remove the directory
521  TString qdir = fQueryDir;
522  qdir = qdir.Remove(qdir.Index(kPROOF_QueryDir)+strlen(kPROOF_QueryDir));
523  qdir = Form("%s/%s/%d", qdir.Data(), qr->GetTitle(), qr->GetSeqNum());
524  PDB(kGlobal, 1)
525  Info("RemoveQuery", "removing directory: %s", qdir.Data());
526  gSystem->Exec(Form("%s %s", kRM, qdir.Data()));
527 
528  // Remove from memory lists
529  if (soft) {
530  TQueryResult *qrn = qr->CloneInfo();
531  Int_t idx = fQueries->IndexOf(qr);
532  if (idx > -1)
533  fQueries->AddAt(qrn, idx);
534  else
535  SafeDelete(qrn);
536  }
537  fQueries->Remove(qr);
538  SafeDelete(qr);
539 
540  // Done
541  return;
542 }
543 
544 ////////////////////////////////////////////////////////////////////////////////
545 /// Locate query referenced by queryref. Return pointer to instance
546 /// in memory, if any, or 0. Fills qdir with the query specific directory
547 /// and qry with the query number for queries processed by this session.
548 
549 TProofQueryResult *TQueryResultManager::LocateQuery(TString queryref, Int_t &qry, TString &qdir)
550 {
551  TProofQueryResult *pqr = 0;
552 
553  // Find out if the request is a for a local query or for a
554  // previously processed one
555  qry = -1;
556  if (queryref.IsDigit()) {
557  qry = queryref.Atoi();
558  } else if (queryref.Contains(fSessionTag)) {
559  Int_t i1 = queryref.Index(":q");
560  if (i1 != kNPOS) {
561  queryref.Remove(0,i1+2);
562  qry = queryref.Atoi();
563  }
564  }
565 
566  // Build dir name for specific query
567  qdir = "";
568  if (qry > -1) {
569 
570  PDB(kGlobal, 1)
571  Info("LocateQuery", "local query: %d", qry);
572 
573  // Remove query from memory list
574  if (fQueries) {
575  TIter nxq(fQueries);
576  while ((pqr = (TProofQueryResult *) nxq())) {
577  if (pqr->GetSeqNum() == qry) {
578  // Dir for specific query
579  qdir = Form("%s/%d", fQueryDir.Data(), qry);
580  break;
581  }
582  }
583  }
584 
585  } else {
586  PDB(kGlobal, 1)
587  Info("LocateQuery", "previously processed query: %s", queryref.Data());
588 
589  // Remove query from memory list
590  if (fPreviousQueries) {
591  TIter nxq(fPreviousQueries);
592  while ((pqr = (TProofQueryResult *) nxq())) {
593  if (queryref.Contains(pqr->GetTitle()) &&
594  queryref.Contains(pqr->GetName()))
595  break;
596  }
597  }
598 
599  queryref.ReplaceAll(":q","/");
600  qdir = fQueryDir;
601  qdir = qdir.Remove(qdir.Index(kPROOF_QueryDir)+strlen(kPROOF_QueryDir));
602  qdir = Form("%s/%s", qdir.Data(), queryref.Data());
603  }
604 
605  // We are done
606  return pqr;
607 }
608 
609 ////////////////////////////////////////////////////////////////////////////////
610 /// Final steps after Process() to complete the TQueryResult instance.
611 
612 Bool_t TQueryResultManager::FinalizeQuery(TProofQueryResult *pq,
613  TProof *proof, TVirtualProofPlayer *player)
614 {
615  if (!pq || !proof || !player) {
616  Warning("FinalizeQuery", "bad inputs: query = %p, proof = %p, player: %p ",
617  pq ? pq : 0, proof ? proof : 0, player ? player : 0);
618  return kFALSE;
619  }
620 
621  Int_t qn = pq->GetSeqNum();
622  Long64_t np = player->GetEventsProcessed();
623  TVirtualProofPlayer::EExitStatus est = player->GetExitStatus();
624  TList *out = player->GetOutputList();
625 
626  Float_t cpu = proof->GetCpuTime();
627  Long64_t bytes = proof->GetBytesRead();
628 
629  TQueryResult::EQueryStatus st = TQueryResult::kAborted;
630 
631  PDB(kGlobal, 2) Info("FinalizeQuery","query #%d", qn);
632 
633  PDB(kGlobal, 1)
634  Info("FinalizeQuery","%.1f %lld", cpu, bytes);
635 
636  // Some notification (useful in large logs)
637  Bool_t save = kTRUE;
638  switch (est) {
639  case TVirtualProofPlayer::kAborted:
640  PDB(kGlobal, 1)
641  Info("FinalizeQuery", "query %d has been ABORTED <====", qn);
642  out = 0;
643  save = kFALSE;
644  break;
645  case TVirtualProofPlayer::kStopped:
646  PDB(kGlobal, 1)
647  Info("FinalizeQuery",
648  "query %d has been STOPPED: %lld events processed", qn, np);
649  st = TQueryResult::kStopped;
650  break;
651  case TVirtualProofPlayer::kFinished:
652  PDB(kGlobal, 1)
653  Info("FinalizeQuery",
654  "query %d has been completed: %lld events processed", qn, np);
655  st = TQueryResult::kCompleted;
656  break;
657  default:
658  Warning("FinalizeQuery",
659  "query %d: unknown exit status (%d)", qn, player->GetExitStatus());
660  }
661 
662  // Fill some variables; in the CPU time we do not include anymore the time
663  // used on the master for preparing and merging, because we want to measure
664  // the efficiency or farction of time useful for work doen by workers
665  PDB(kGlobal, 1)
666  Info("FinalizeQuery", "cpu: %.4f, saved: %.4f, master: %.4f",
667  cpu, pq->GetUsedCPU() ,GetCpuTime());
668 // pq->SetProcessInfo(np, cpu - pq->GetUsedCPU() + GetCpuTime());
669  // We take the difference because this is the total CPU time of the session
670  pq->SetProcessInfo(np, cpu - pq->GetUsedCPU());
671  pq->RecordEnd(st, out);
672 
673  // Save the logs into the query result instance
674  AddLogFile(pq);
675 
676  // Done
677  return save;
678 }
679 
680 ////////////////////////////////////////////////////////////////////////////////
681 /// Save current query honouring the max number of queries allowed
682 
683 void TQueryResultManager::SaveQuery(TProofQueryResult *pq, Int_t mxq)
684 {
685  // We may need some cleanup
686  if (mxq > -1) {
687  if (fQueries && fKeptQueries >= mxq) {
688  // Find oldest completed and archived query
689  TQueryResult *fcom = 0;
690  TQueryResult *farc = 0;
691  TIter nxq(fQueries);
692  TQueryResult *qr = 0;
693  while (fKeptQueries >= mxq) {
694  while ((qr = (TQueryResult *) nxq())) {
695  if (qr->IsArchived()) {
696  if (qr->GetOutputList() && !farc)
697  farc = qr;
698  } else if (qr->GetStatus() > TQueryResult::kRunning && !fcom) {
699  fcom = qr;
700  }
701  if (farc && fcom)
702  break;
703  }
704  if (!farc && !fcom) {
705  break;
706  } else if (farc) {
707  RemoveQuery(farc, kTRUE);
708  fKeptQueries--;
709  farc = 0;
710  } else if (fcom) {
711  RemoveQuery(fcom);
712  fKeptQueries--;
713  fcom = 0;
714  }
715  }
716  }
717  if (fKeptQueries < mxq) {
718  SaveQuery(pq);
719  fKeptQueries++;
720  } else {
721  TString emsg;
722  emsg.Form("Too many saved queries (%d): cannot save %s:%s",
723  fKeptQueries, pq->GetTitle(), pq->GetName());
724  if (gProofServ) {
725  gProofServ->SendAsynMessage(emsg.Data());
726  } else {
727  Warning("SaveQuery", "%s", emsg.Data());
728  }
729  }
730  } else {
731  SaveQuery(pq);
732  fKeptQueries++;
733  }
734 }