Python script stops, no errors giving

南楼画角 提交于 2019-12-24 07:31:39

问题


I have an python script that needs to be running all the time. Sometimes it can run for a hole day, sometimes it only runs for like an hour.

    import RPi.GPIO as GPIO
import fdb
import re

con = fdb.connect(dsn='10.100.2.213/3050:/home/trainee2/Desktop/sms', user='sysdba', password='trainee') #connect to database
cur = con.cursor() #initialize cursor 

pinnen = [21,20,25,24,23,18,26,19,13,6,27,17] #these are the GPIO pins we use,  they are the same on all PI's! We need them in this sequence.
status = [0] * 12 #this is an empty array were we'll safe the status of each pin
ids = []
controlepin = [2] * 12 #this array will be the same as the status array, only one step behind, we have this array so we can know where a difference is made so we can send it
GPIO.setmode(GPIO.BCM) #Initialize GPIO

getPersonIDs()  #get the ids we need


for p in range(0,12):
    GPIO.setup(pinnen[p],GPIO.IN) #setup all the pins to read out data

while True: #this will repeat endlessly
    for e in range(0,12):
        if ids[e]: #if there is a value in the ids (this is only neccesary for PI 3 when there are not enough users
            status[e] = GPIO.input(pinnen[e]) #get the status of the GPIO. 0 is dark, 1 is light
            if (status[e] != controlepin[e]): #if there are changes 
                id = ids[e]
                if id != '': #if the id is not empty
                    if status[e] == 1: #if there is no cell phone present
                        cur.execute("INSERT INTO T_ENTRIES (F_US_ID, F_EN_STATE) values (? ,0)",(id)) #SEND 0, carefull! Status 0 sends 1, status 1 sends 0 to let it make sense in the database!!
                    else :
                        cur.execute("INSERT INTO T_ENTRIES (F_US_ID, F_EN_STATE) values (? ,1)",(id))

                con.commit() #commit your query
                controlepin[e] = status[e]  #safe the changes so we woulnd't spam our database  
    time.sleep(1) #sleep for one second, otherwise script will crash cause of while true


def getPersonIDs(): #here we get the IDS 
    cur.execute("SELECT first 12 A.F_US_ID FROM T_RACK_SLOTS a order by F_RS_ID;") #here is where the code changes for each pi
    for (ID) in cur:
        ids.append(ID) #append all the ids to the array

The script is used for a cellphone rack, through LDR's I can see if a cellphone is present, then I send that data to a Firebird database. The scripts are running om my Raspberry PI's.

Can it be that that the script just stops if the connection is lost for a few seconds? Is there a way to make sure they query's are always send?


回答1:


Can it be that that the script just stops if the connection is lost for a few seconds?

More so, the script IS stopping for every Firebird command, including con.commit() and it only continues when Firebird processes the command/query.

So, not knowing much of Python libraries I would still give you some advices.

1) use parameters and prepared queries as much as you can.

 if status[e] == 1: #if there is no cell phone present
                        cur.execute("INSERT INTO T_ENTRIES (F_US_ID, F_EN_STATE) values (? ,0)",(id)) #SEND 0, carefull! Status 0 sends 1, status 1 sends 0 to let it make sense in the database!!
                    else :
                        cur.execute("INSERT INTO T_ENTRIES (F_US_ID, F_EN_STATE) values (? ,1)",(id))

That is not the best idea. You force Firebird engine to parse the query text and build the query again and again. Waste of time and resources.

The correct approach is to make INSERT INTO T_ENTRIES (F_US_ID, F_EN_STATE) values (?,?) query, then prepare it, and then run the already prepared query changing the parameters. You would only prepare it once, before the loop, and then would run it many times.

Granted, I do not know how to prepare queries in the Python library, but I think you'd find the examples.

2) do not use SQL server for saving every single data element you get. It is a known mal-practice, that was suggested again decade ago. Especially with lazy versioned engine Interbase/Firebird is.

The thing is, with every your statement Firebird checks some internal statistics and sometimes it decides time came to do housekeeping.

For example, your select statement is akin for garbage collection. Firebird might stop for scanning all the table, find the orphaned obsolete versions of rows and clear them away. For example your insert statement is akin for index recreation: if Firebird would think the B-Tree of the index is got too one-sided, it would drop it, and build a new balanced tree, reading out the whole table ( and yes, reading the table may provoke GC on top of tree recreation ).

More so, let as steer away from Firebird specifics - what would you do ig Firebird crashes? Just crashes, it is a program, and like every program it may have bugs. Or for example you run out of disk space and Firebird can no more insert anything into the database - where would your hardware sensors data end in then? Won't it just be lost ?

http://www.translate.ru - this one works usually better than Google or Microsoft translation, especially if you set the vocabulary to computers.

See #7 at http://www.ibase.ru/dontdoit/ - "Do not issue commit after every single row". #24 at https://www.ibase.ru/45-ways-to-improve-firebird-performance-russian/ suggests committing packets of about a thousand rows as a sweet spot between to many transactions and too much uncommitted data. Also check #9, #10, #16 and #17 and #44 at the last link.

The overall structure of your software complex I believe has to be split into two services.

  1. Query data from hardware sensors and save it to plain stupid binary flat file. Since this file is the most simplistic format that can be - the performance and reliability would be maxed.
  2. Take ready binary files, and insert them into SQL database in bulk insert mode.

So, for example, you set the threshold of 10240 rows.

  1. The service #1 creates the file "Phones_1_Processing" with BINARY well-defined format. It also creates and opens "Phones_2_Processing" file, but keeps it at 0 length. Then it keeps adding rows into the "Phones_1_Processing" for a while. It might also flush OS file buffers after every row, or every 100 rows, or something that would get best balance between reliability and performance.
  2. When the threshold is met, the service #1 switches into recording incoming data cells into the already created and opened "Phones_2_Processing" file. It can be done instantly, change one file handler type variable in your program.
  3. Then the service #1 closes and renames "Phones_1_Processing" into ""Phones_1_Complete".
  4. Then the service #1 creates new empty file "Phones_3_Processing" and keeps it open with zero length. Now it is back at state "1" - ready to instantly switch its recording into new file, when the current file is over.

The key points here is that the service should only do most simple and most fast operations. Since any random delay would mean your realtime-generated data is lost and would never be recovered. BTW, how can you disable Garbage Collection in Python, so it would not "stop the world" suddenly? Okay, half-joking. But the point is kept. GC is random non-deterministic bogging down of your system, and it is badly compatible with regular non-buffered hardware data acquisition. That primary acquisition of non-buffered data is better be done with most simple=predictable services, and GC is a good global optimization, but the price is it tends to generate sudden local no-service spikes.

As this all happens with Service #1 with have another one.

  1. Service #2 keeps monitoring data changes in the folder you use to save primary data. It subscribes to "some file was renamed" events and ignores others. Which service to use? ask Linux guru. iNotify, dNotify, FAM, Gamin, anything of a kind that would suit.
  2. When Service #2 is awaken with "file was renamed and xxxx is new name" it checks if the new file name ends with "_Complete". If it does not - then that was a bogus event.
  3. When the event is for a new "Phone_...._Complete" file, then it is time to "bulk insert" it into Firebird. Google for "Firebird bulk insert", for example http://www.firebirdfaq.org/faq209/
  4. The Service #2 renames "Phone_1_Complete" into "Phone_1_Inserting", so the state of data packet is persisted (as file name).
  5. The Service #2 attaches this file into Firebird database as an EXTERNAL TABLE
  6. The Service #2 proceeds with bulk insert, as described above. De-activating indexes, it opens a no-auto-undo transaction and keeps pumping the rows from the External Table into the destination table. If the service or server crashes here - you have a consistent state: transaction gets rolled back and the file name shows it still is pending to be inserted.
  7. When all the rows are pumped - frankly, if Python can work with binary files, it would be a single INSERT-FROM-SELECT, - you commit the transaction, delete the External Table (detaching firebird from the file), then rename the "Phone_1_Inserting" file into "Phone_2_Done" (persisting its changed state) and then you delete it.
  8. Then the service #2 looks if there are new "_Complete" files already ready in the folder, and if not, it goes into step 1 - sleeps until FAM event would awake it

All in all, you should DECOUPLE your services.

https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29

The service who's main responsibility is to be with not a tiny pause ready to get and save data flow, and another service whose responsibility transfer the saved data into SQL database for ease of processing and it is not a big issue if it sometimes makes delays for few seconds as long as it does not lose data in the end.



来源:https://stackoverflow.com/questions/44305998/python-script-stops-no-errors-giving

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!