MySQL: Werte werden zurückgegeben, die eigentlich noch garnicht existieren dürften

mapcado

Grünschnabel
Hallo zusammen, ich habe ein recht seltsames Problem:

Ich habe ein Script geschrieben, welches von einem externen Webdienst Artikel herunterlädt, in einer MySQL-Tabelle speichert, und anschliessend AJAX-mäßig an den Client überträgt.

Da das ganze aufgrung einer hohen Anzahl an Calls an den Webdienst (teilweise über 100 Stück) relativ lange dauern kann, habe ich eine Möglichkeit zum Status-Update erstellt: Jedesmal, wenn Artikel bei meinem Script ankommen, wird der Status aktualisiert, indem nachgesehen wird, wieviele Artikel jetzt insgesamt da sind, und wie viele gebraucht werden - Das alles natürlich nachdem die Artikel in die Datenbank geschrieben wurden. Der Client fragt über AJAX ständig den Status ab, welcher hübsch in Form eines Balkens angezeigt wird, und holt, sobald er bei 100% angekommen ist, die Artikel vom Server ab.

So, und jetzt zu meinem Problem: Der Status ist jedesmal schon bei 100% bevor alle Artikel angekommen sind. Bei wenigen Calls an den Webdienst (in meinem Fall nur einer) bekommt der Client somit manchmal garkeine Artikel, und manchmal nur einen Teil davon. Ich habe dem Client daher daher jedesmal einen timestamp mitgeschickt (ich weiß, ist nicht unbedingt Debugging der Königsklasse), und dabei folgendes bemerkt:

Status-Update: [...] 614.6392 Sekunden (Ausgabe kurz bevor der Query abgeschickt wurde)
Beim Client Status=100%: [...] 612.2205 Sekunden (also 2 Sekunden vor dem Status-Update)
Artikel-Abholung vom Client: [...] 612.462 Sekunden (folgerichtig kurz nachdem Status=100% beim Client angekommen ist. Allerdings werden keine Artikel übertragen)

Das alles muss irgendwie damit zu tun haben, dass für das Status-Update und die Status-Abfrage vom Client natürlich zwei unterschiedliche Datenbankverbindungen genutzt werden, und die Datenbankverbindung 1, die auch das Einfügen der Artikel übernimmt, noch nicht geschlossen wurde. Auch die satten 2 Sekunden Zeitverzögerung kann ich mir einfach nicht erklären, ich steh also echt aufm Schlauch...

Daher meine Frage an die MySQL-Experten unter euch: Woran kann es liegen, dass ein Datenbankeintrag über 2 Sekunden bevor er eigentlich gemacht werden sollte, ausgeführt wird? Und was kann man dagegen tun?

PS: Es liegt übrigens nicht an meinem Script. Ist mir natürlich auch schon in den Sinn gekommen, dass da irgendwo vorher am Status rumgepfuscht wird. Deshalb habe ich die Tabellenspalte einfach umbenannt - mit dem gleichen Ergebnis...
 
Also erstens mal, ich habe so gut wie nix verstanden, von dem, was du beschrieben hast. Mag am fehlenden Code oder meiner fehlenden Fantasie liegen.

Zweitens: Wenn du der Meinung bist, das es auf gar keinen Fall mit deinem PHP-Script zu tun hat, warum postest du die Frage dann überhaupt im PHP-Forum und nicht statt dessen im MySQL-Forum?

Weshalb es nicht am PHP-Script liegen kann, nur weil man eine Tabelle umbenennt, kann ich grad auch nicht nachvollziehen.

Deine Fehlerbeschreibung ist alles andere als hilfreich, es gibt keinen Zustand, bei dem ein Datensatz in eine Kalkulation (Fortschrittsanzeigen-Berechnung ist natürlich auch eine Kalkulation) einbezogen werden kann, den es noch nicht gibt. Der PHP-/MySQL-Flux-Kompensator wurde schließlich noch nicht erfunden (bin mir da ziemlich sicher).

Du hast jetzt zwei, vielleicht drei Möglichkeiten:

- Du lässt dein Post in Relationelle Datenbanken verschieben und hoffst, das jemand deiner Schlussfolgerung nachfolgen kann.
- Du postest ein bisschen Code zusammen mit der Tabellen-Struktur und wir schauen, ob wir das Problem gelöst bekommen.
- Du probierst es morgen noch mal, vielleicht sind dann alle Datensätze auf unerklärliche Weise angelegt und dein Problem aus der Welt.

Sorry für den Sarkasmus, ist nicht persönlich oder böse gemeint. Aber hier sind wir nun mal im PHP-Forum und da brauchts manchmal das eine oder andere Stückchen Code um das Problem verstehen zu können.
 
Also, erstmal vielen Dank für deine Antwort, aber das mit dem Forum zu relationalen DBs habe ich leider übersehen, und da auch hier häufig Fragen zu MySQL gestellt und beantwortet werden, dachte ich mir, das hier wäre der richtige Platz.

Zu dem fehlenden Code: Du hast natürlich Recht, aber ich dachte, dadurch würde das hier alles zu umfangreich. Also nochmal.

Es gibt insgesamt 4 relevante Scripte
1. db_credentials.php (öffnet die Datenbankverbindung, und wird in allen untenstehenden Scripten includiert)
2. fetchArticles.php (sorgt für das übertragen der Artikel vom externen Webdienst, dsowie das Schreiben dieser in die MySQL-Tabelle und natürlich den Status-Update)
3. getStatus.php (holt sich einfach den Status in Prozent aus der Tabelle gibt ihn zurück)
4. getArticles.php (überträgt alle empfangenen Artikel, wird bei Status=100% aufgerufen)

Sowie 2 relevante Tabellen
1. requests beinhaltet Informationen für den Request, z.B. die Such-Optionen (Suchbegriffe, Maximalpreis, Kategorie, usw.). Was und hier interessiert, ist die ID und der Status.
Code:
id|keywords|maxprice|...|status

2. items beinhaltet Informationen für die übertragenen Artikel, z.B. Titel, Bild-URL, Preis, usw. Was und hier interessiert, ist die request_id.
Code:
id|request_id|title|galleryurl|price|...
Durch die Request-ID werden die Artikel mit der ersten Tabelle verknüpft. Ausserdem findet die gesamte Kommunikation zwischen Client und Server mit Hilfe dieser ID statt.

Jatzt zum Inhalt der 4 Scripte (exemplarisch)
db_credentials.php
Code:
$db = new mysqli($host, $user, $db_pass);
$db->select_db($db_name);

fetchArticles.php - der Teil, der ausgeführt wird, sobald eine neue Antwort angekommen ist
Code:
include_once(db_credentials.php)

[...]

// Callback-Function, die aufgerufen wird, sobald eine Antwort vom Webservice angekommen ist
function callback($xml_result)
{
  write_items($xml_result); // Schreibe die Artikel in die Tabelle

  // Jetzt das Status-Update

  // zuerst die Artikel zählen, die schon da sind
  $query = "SELECT count(*) as number FROM items WHERE request_id = {$request_id}";
  if($response = $db->query($query) && $entity = $response->fetch_array())
  {
    $no_items_found = $entity['number'];
    $status_dec = 1.0 * $no_items_found / 50; // 50 Artikel sollen insgesamt gefunden werden
    $status_perc =min(100, round(100 * $status_dec));

    // gebe einen Timestamp aus. Erklärung siehe unten
    echo microtime(1);
    // Nun wird der Status upgedatet
    $query = "UPDATE requests SET status = {status_perc} WHERE id = {request_id}";
    $db->query($query);
  }
}

[...]

$db->close();

getStatus.php
Code:
header("Content-type: text/plain;");
include_once('db_credentials.php');

// Mittels "GET" wird die Request-ID vom Client übertragen
if(isset($_GET['id']))
{
  $query = "SELECT status FROM requests WHERE id = " . ($_GET['id']);
  if($response = $db->query($query) && $entity = $response->fetch_array())
  {
    $status = $entity['status'];
    $result = array('status' =>  $status, 'time' => microtime(1)); // Auch hier wieder eine Zeitausgabe
    echo json_encode($result);
  }
}

$db->close();

getArticles.php
Code:
header("Content-type: text/plain;");
include_once('db_credentials.php');

$item_infos = array("price", "title", ...); // Die Namen aller Informationen, die übertragen werden sollen

$items = array(); // Hier kommen die Artikelinformationen hinein

// Wieder die Request-ID mittels "GET"
if(isset($_GET['id']))
{
  $query = "SELECT * FROM items WHERE request_id = " . ($_GET['id']);
  if($response = $db->query($query))
  {
    $i = 0;
    while($entity = $response->fetch_array())
    {
      $items[$i] = array();
      foreach($entity as $key => $value)
      {
        if(in_array($key, $item_infos))
        {
          $items[$i][$key] = $value;
        }
      }
      $i++;
    }
  }
}

$result = array('items' => items, 'time' => microtime(1)); // Die dritte Zeitausgabe
echo json_encode($result);

$db->close();

So, jetzt nochmal kurz eine Ablaufbeschreibung: Der Client ruft zunächst ein hier nicht erwähntes Script auf, um ihm die Such-Optionen mitzuteilen, und eine Request-ID zugewiesen zu bekommen. Dieses Script schreibt alle Informationen in die Tabelle "requests". Anschliessend ruft der Client "fetchArticles.php" auf, direkt daraufhin fängt er an, in einer Endlosschleife immer wieder "getStatus.php" aufzurufen, womit er erst aufhört, wenn er 100% zurück bekommt. Dann ruft er "getArticles.php" auf, um die Artikel zu übertragen.

Jetzt das Problem: Es sind bereits ein paar Sekunden nachdem Status=100% zurückgegeben wurde, manchmal immer noch keine Artikel von "getArticles.php" gefunden worden.

Deshalb das in meinem vorigen Post erwähnte Experiment mit dem Timestamp. Ich habe einfach bei jedem Aufruf einen Timestamp mitgeschickt. Das Ergebnis:
Status-Update: in fetchArticles.php [...] 614.6392 Sekunden (Ausgabe kurz bevor der Query abgeschickt wurde)
Beim Client Status=100%: in getStatus.php [...] 612.2205 Sekunden (also 2 Sekunden vor dem Status-Update)
Artikel-Abholung vom Client: in getArticles.php [...] 612.462 Sekunden (kurz nach der Meldung "Status=100%", aber keine Artikel wurden in der db gefunden)


So, ich hoffe, man kann das Problem jetzt verstehen. Ich hätte nur nicht gedacht, dass sich jemand hier einen solch langen Text durchlesen will.
 
Ich bin echt ein ungeheurer Depp. Das ganze lag am Browser-Caching. Habe jetzt die Header folgendermaßen modifiziert:
Code:
header("Expires: " . gmdate("D, d M Y H:i:s", 0) ." GMT"); // Laeuft am 1.1.1970 ab
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // wurde jetzt zum letzten Mal veraendert
header("Cache-Control: no-store, no-cache, must-revalidate"); // darf nicht gecacht werden
header("Pragma: no-cache"); // irgendwas fuer aeltere Internet-Explorer

Und siehe da - es funktiuoniert. Mannomann - dafür habe ich fast einen ganzen Tag an Zeit verschwendet.
 
Zurück