unable to open database file nach x aufrufen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Hi,

Ich bastel momentan gerade an einem kleinen "webserver" für Private zwecke. Dafür benutze ich cherrypy und das ganze funktioniert auch einwandfrei, allerdings habe ich ein problem mit der sqlite3 datenbank.

Wenn ich eine Seite öffne, die nur 10 Einträge ausliest funktioniert alles bestens, einträge werden angezeigt etc. sobald das query allerdings eine gewisse anzahl an abfragen überschreitet bekomme ich die meldung:
OperationalError: unable to open database file
Da hilft dann auch nichts mehr außer das Script neu zu starten :(

Gibts ne maximale anzahl an abfragen bevor die db "dicht" macht? oder hab ich nen anderes problem?
Ordner sowie db sind bereits auf 777 gesetzt. Ich kann mir nicht erklären wieso es geht aber ab einer gewissen anzahl einfach nicht mehr will.

Ich verwende python 2.7.3
Benutzeravatar
sparrow
User
Beiträge: 4505
Registriert: Freitag 17. April 2009, 10:28

Wie genau stellst du denn die Verbindung zur Datenbank her? Selbst ein Exklusiver Lock auf die Datenbank würde beim Öffnen nicht diese Fehlermeldung bringen (das bringt dann nämlich gar keine).

Ich kenne mich mit dem Framework nicht aus, wird dort der Zugriff zentral konfiguriert?

Da Query und Abfrage identisch sind, was meinst du mit "sobald das query allerdings eine gewisse anzahl an abfragen überschreitet"?
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Die Verbindung zur DB läuft über ne extra Klasse:
https://raw.github.com/cytec/SynoDLNAtr ... rakt/db.py

aufgerufen wird das dann so:

Code: Alles auswählen

myDB = db.DBConnection()
result = myDB.select(u"SELECT * from books ORDER BY sort ASC")
nun gehts weiter und ich parse jeden eintrag und lade dafür andere inhalte aus der db:

Code: Alles auswählen

for book in result:
    #weiter 5 abfragen
"sobald das query allerdings eine gewisse anzahl an abfragen überschreitet" soll heißen:
sobald ich mehr als x abfragen aus/von der DB mache, bekomme ich eben diesen fehler

ich habe mal "mitgezählt" nach 2550 aufrufen der db macht die dicht... is evtl auch etwas heftig aber die db is leider so angelegt dass ich um alle infos für einen eintrag zu bekommen 5-7 abfragen schicken muss ...
Benutzeravatar
sparrow
User
Beiträge: 4505
Registriert: Freitag 17. April 2009, 10:28

Kann es sein, dass du die Datenbankverbindungen nicht wieder schließt?

Du brauchst 5-7 Abfragen für jeden Eintrag? Das würde mich sehr überraschen.
Bist du sicher, dass dein Datenbank-Design in Ordnung ist? Unter Umständen kann man auch die Abfragen deutlich mit Aggregarfunktionen und Joins vereinfachen.
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Die DB sollte eigentlich durch die Klasse wieder geschlossen werden nach dem aufruf.

Die DB struktur is relativ komplex, aber da hab ich leider kein Einfluss drauf, atm sieht das in etwa so aus:

aus der tabelle "books" werden infos geladen, mit der id aus "books" wird die author id aus "books_authors_link" geladen mit der author id dann der author aus "authors" das ganze dann noch für tags, genre, beschreibung, buchserien, preis etc es gibt immer eine books_xxx_links mit der book id und der zugehörigen id von xxx...

ich wüsste nicht wie ich das vereinfachen könnte, bin da aber für jeden tipp offen :)
Benutzeravatar
sparrow
User
Beiträge: 4505
Registriert: Freitag 17. April 2009, 10:28

Den Fehler, den du hier beschreibst, tritt unter anderem dann auf, wenn du zu viele Connections auf die Datenbank öffnest.

Zeig mal die die weiteren 5 Abfragen, wie du die ausführst. Machst du da jedesmal eine neue DBConnection() auf? In der verlinkten Klasse sehe ich nämlich nirgends ein Close.

Auch auf "komplexe Datenbanken" kann man mit per "join" verbundenen Abfragen zugreifen. Dafür sind relationale Datenbanken ja da.
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Ich hab die verlinkte klasse etwas angepasst und dort ein close eingebaut.
Allerdings ändert das bisher nichts am ergebnis.
sparrow hat geschrieben: Zeig mal die die weiteren 5 Abfragen, wie du die ausführst. Machst du da jedesmal eine neue DBConnection() auf? In der verlinkten Klasse sehe ich nämlich nirgends ein Close.
Ja es wird jedes mal eine neue verbindung aufbemacht, die weiteren aufrufe laufen alle nach dem folgenden schema ab:

Code: Alles auswählen

	def _getBookSeries(self, book_id):
		myDB = db.DBConnection()
		bookids = myDB.select(u"SELECT * from books_series_link WHERE book = '{0}'".format(book_id))
		#return bookids
		series = {}
		for bookid in bookids:
			series["id"] = bookid["series"]
			series_result = myDB.select(u"SELECT * from series WHERE id = '{0}'".format(bookid["series"]))
			series["series_name"] = series_result[0]["name"]
		if series:
			return series["series_name"]
		else:
			return None
hier die for schleife:

Code: Alles auswählen

for book in result:
		bookinfos = self._getBookInfoFromId(book_id)
		bookinfos["rating"] = self._getBookRating(book_id)
		bookinfos["publisher"] = self._getPublisher(book_id)
		bookinfos["description"] = self._getSummary(book_id)
		bookinfos["format"] = self._getFormat(book_id)
		bookinfos["author"] = self._getAuthor(book_id)
		bookinfos["series_name"] = self._getBookSeries(book_id)
		bookinfos["tags"] = self._getTags(book_id)

sparrow hat geschrieben: Auch auf "komplexe Datenbanken" kann man mit per "join" verbundenen Abfragen zugreifen. Dafür sind relationale Datenbanken ja da.
Hast du da evtl nen Tipp für mich bzw ne quelle zum nachschlagen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du solltest Dir dringend mal SQLAlchmey angucken!

Sehe ich das richtig, dass Du ein *globales* `db`-Objekt hast? Genau das wäre für uns wohl interessant gewesen - denn dort werden ja offensichtlich Connections erstellt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Der sinn war nen Globales ojekt zu haben, damit ich nicht jedes mal die komplette connection und das schließen der DB beachten muss wenn ich ne abfrage starte.
Benutzeravatar
sparrow
User
Beiträge: 4505
Registriert: Freitag 17. April 2009, 10:28

Ach du wei, die verlinkte Klasse ist von dir? Ich dachte die wäre vielleicht aus irgend einem Projekt.

Wenn du das immer nach dem "Schema" machst, dann öffnest du ja doch jedes mal eine Connection, und das geht halt nur eine begrenzte Anzahl. Wahrscheinlich abhängig vom max. Filehandle des Betriebssystem.

Hyperion hat recht, schau dir SQLAlchemy an. Das macht dir das Leben extrem viel leichter.
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

ne die klasse is von was anderem ich hab die nur kopiert und etwas angepasst..

werd mir sqlalchemy mal anschauen, hatte das mal kurz angetestet aber leider hat das kein unterschied gemacht...

Ich sollte evtl so oder so über eine methode nachdenken wie ich mit weniger DB abfragen klar komme, das kostet ja auch jedes mal nen haufen zeit :)

Danke für die Hilfe soweit ich werd mir sqlalchemy auf jeden fall mal anschauen.
Benutzeravatar
sparrow
User
Beiträge: 4505
Registriert: Freitag 17. April 2009, 10:28

Nur damit wir uns richtig verstehen:
Jedesmal wenn du myDB = db.DBConnection() aufrufst, wird eine neue Connection aufgemacht. In der von dir verlinkten Klasse wird sie aber nicht mehr geschlossen, die Connection besteht also mindesten so lange, wie das Objekt besteht.
Leider sieht man nur Schnipsel deines Codes, aber so wie ich dich verstehe steht die Zeile in jeder deiner _getirgendwas-Methoden. Also viele, viele Connections, die da nicht hingehören.

Wenn man mal von der etwas seltsamen Klasse absieht, die du da verwendest: es sollte schon helfen den _getirgendwas-Methoden die bereits instanzierte Klasse mitzugeben. Also _getirgendwas(bookid, con), wobei con eine Instanz von DBConnection ist.
Ruffy
User
Beiträge: 34
Registriert: Dienstag 2. Oktober 2012, 11:26

Ich hab mal alles was ich an code habe hochgeladen, werd mir mal anschauen wies mit sqlalchemy aussieht.

https://bitbucket.org/raphaelmutschler/calibreserver/
is mein erstes python projekt daher isses wahrscheinlich kraut und rüben :D
BlackJack

@Ruffy: Ich würde dann erst einmal auf PEP 8 -- Style Guide for Python Code verweisen was die Namensgebung und das Einrücken betrifft.

Dann wurde ja schon gesagt, dass das zu viele Anfragen sind. Setz Dich mal mit SQL auseinander. Man kann mit einer Anfrage Daten aus mehr als einer Tabelle abfragen und miteinander verknüpfen. Das Stichwort ``JOIN`` wurde ja schon genannt.

Es ist sehr deutlich viel Copy & Paste „Programmierung” erkennbar. Du solltest mal bewusst auf die entsprechende Editor-Funktion verzichten und den Quelltext tatsächlich *schreiben*. Dann denkst Du sicher mehr darüber nach was Du tust und wie man das ständige Wiederholen von Mustern vermeiden kann. Viele der `Book._get*()`-Methoden sind *sehr* ähnlich aufgebaut. Das gleiche gilt für die `list*()`-Methoden. Nebenbei sind das alles keine Methoden — `Book` ist keine Klasse, sondern fasst einfach nur einen Haufen Funktionen zusammen. Das ist aber die Aufgabe von Modulen.

Durch das Sternchen nach dem ``SELECT`` werden viel mehr Daten abgefragt als in den meisten der `_get*()`-Methoden dann auch tatsächlich verarbeitet werden.

In `searchTitle()` wird `_generateBook()` mit der falschen Anzahl von Argumenten aufgerufen. Ausserdem wird dort und in `searchRating` auf `myDB` zugegriffen ohne dass der Name vorher an einen Wert gebunden wird.

Die `_init_()`-Methode ist sinnfrei und der Name falsch geschrieben.

Von den fünf Importen im `calibre.books`-Modul wird nur *einer* tatsächlich verwendet.

Im `db`-Modul gibt es einiges was da wohl auch durch kopieren von einem anderen Projekt hingekommen ist, was man aber nicht braucht.

`dict_factory()` existiert zweimal. Die „Methode” sollte verschwinden und bei `DBConnection.__init__()` sollte man bei `row_type` direct die Funktion übergeben statt den Umweg über eine Zeichenkette und einem ``if`` zu gehen.

Aber eigentlich sollte das alles SQLAlchemy weichen.
Antworten