Is Postgres Implementation Thread Safe?

Everything else that doesn't fall into one of the other PB categories.
swhite
Enthusiast
Enthusiast
Posts: 726
Joined: Thu May 21, 2009 6:56 pm

Is Postgres Implementation Thread Safe?

Post by swhite »

Hi

I am wondering whether the Postgres implementation in Purebasic is thread safe? I created a ISAPI extension using Purebasic and compiled it with the "Create Thread safe" flag set but I get errors from time to time such as "extraneous data in "T" message" or "insufficient data in "T" message
". I believe this maybe due to the fact that my ISAPI extension is running in a multithreaded environment.

Perhaps I should open a new connection HttpExtensionProc instead of opening the database connection in AttachProcess.

Thanks,
Simon
Simon White
dCipher Computing
Fred
Administrator
Administrator
Posts: 16619
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Is Postgres Implementation Thread Safe?

Post by Fred »

It should be, the lib is compiled is compiled with ENABLE_THREAD_SAFETY define, and the PB lib is threadsafe.
swhite
Enthusiast
Enthusiast
Posts: 726
Joined: Thu May 21, 2009 6:56 pm

Re: Is Postgres Implementation Thread Safe?

Post by swhite »

Okay thank-you for that information. I will change the way I handle connections and see if my problem goes away.

Simon
Simon White
dCipher Computing
swhite
Enthusiast
Enthusiast
Posts: 726
Joined: Thu May 21, 2009 6:56 pm

Re: Is Postgres Implementation Thread Safe?

Post by swhite »

I changed my code to open a new connection on each ISAPI request and I stopped getting the Postgres error messages. I installed pgBouncer for connection polling so I do not have to have the cost of creating a new connection on every request. So far things are working smoothly.

Thanks,
Simon
Simon White
dCipher Computing
Fred
Administrator
Administrator
Posts: 16619
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Is Postgres Implementation Thread Safe?

Post by Fred »

Nice to hear !
swhite
Enthusiast
Enthusiast
Posts: 726
Joined: Thu May 21, 2009 6:56 pm

Re: Is Postgres Implementation Thread Safe?

Post by swhite »

Hi

It appears I may have spoken too soon. I get many less errors now but I do get some like the following:

Code: Select all

select pid,site from dcfusnsrvc.khsite where siteid='BVDCORN55400' failed due To error: no connection to the server
Which occurred immediately after successfully creating the connection to the database.

or

Code: Select all

Error : C0000005 on line 947 in C:\Business\dCipherComputing\Applications\FusionPro\PB_Windows\dCFusionREST.pb
Line 947 is

Code: Select all

 ProcedureReturn #HSE_STATUS_SUCCESS

Code: Select all

; This is the main function that is called for each client request
; *pECB contains all needed data
;
ProcedureDLL HttpExtensionProc(*pECB._EXTENSION_CONTROL_BLOCK)
   
   Define lcJSON.s,lnSize.i,lnBytes.i,lnLen.i,*Ascii,*Header,*Status,lcTrn.s,lcHTM.s
   Define Header._HSE_SEND_HEADER_EX_INFO
   ;
   ; Send the response header with the ServerSupportFunction() callback
   ;       
   Header._HSE_SEND_HEADER_EX_INFO
   *Status = AllocateMemory(100) 
   *Header = AllocateMemory(100)
   PokeS(*Status, "200 OK", 6, #PB_Ascii)
   PokeS(*Header, "Content-type: application/json" + #CRLF$ + #CRLF$, 34, #PB_Ascii)
   Header\pszStatus = *Status
   Header\pszHeader = *Header   
   Header\cchStatus = 6  ; Length of Status
   Header\cchHeader = 34 ; Length of Header
   Header\fKeepConn = 0 
   
   goParam\Sec = ElapsedMilliseconds()
   goParam\Rcvd = ReplaceString(RemoveString(GetRestData(*pECB),Chr(13)),Chr(39),Chr(34)) ; PB expects JSON to use double quotes
   goParam\URL = LCase(ServerVariable(*pECB,"HTTP_URL"))
   goParam\Port=ServerVariable(*pECB,"SERVER_PORT")
   goParam\Auth=ServerVariable(*pECB,"HTTP_AUTHENTICATION")
   goParam\SiteID=ServerVariable(*pECB,"QUERY_STRING")
   goParam\Method=ServerVariable(*pECB,"REQUEST_METHOD")
   goParam\dBNo=OpenDatabase(#PB_Any,goParam\dBName,goParam\dBUser,goParam\dBPwd,#PB_Database_PostgreSQL)
   If Not goParam\dBNo
      goParam\dBNo=OpenDatabase(#PB_Any,goParam\dBName,goParam\dBUser,goParam\dBPwd,#PB_Database_PostgreSQL)
   EndIf
   If Not goParam\dBNo
      WriteToLog("**ERROR HTTPExtensionProc("+goParam\dBName+" uid="+goParam\dBUser+" pwd="+goParam\dBPwd+") failed To open connection.")
   ElseIf Not IsAuthenticated(@goParam)
      PokeS(*Status, goParam\Msg,StringByteLength(goParam\Msg,#PB_Ascii), #PB_Ascii)      
      Header\cchStatus = 16
      lcJSON = ~"{\"msg\":\"401 Unauthorized - "+goParam\Method+goParam\URL+~"\"}"
   ElseIf goParam\Method+goParam\URL = "POST/rest/prices.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      SavePrices(goParam)
      lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\"}"
   ElseIf goParam\Method+goParam\URL = "POST/rest/transactions.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      lcTrn = GetTransactions(goParam)
      If lcTrn = ""
         lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"transactions\":[]}" ; Javascript does not expect the array without quotation marks.
      Else
         lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"transactions\":"+lcTrn+"}" ; Javascript does not expect the array without quotation marks.
      EndIf
   ElseIf goParam\Method+goParam\URL = "POST/rest/transactionexport.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      lcTrn = GetTransactionExport(goParam)
      If lcTrn = ""
         lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"transactions\":[]}" ; Javascript does not expect the array without quotation marks.
      Else
         lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"transactions\":"+lcTrn+"}" ; Javascript does not expect the array without quotation marks.
      EndIf
   ElseIf goParam\Method+goParam\URL = "POST/rest/receipt.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      lcHTM = PrintReceipt(goParam)
      lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"receipt\":\""+lcHTM+~"\"}"
   ElseIf goParam\Method+goParam\URL = "POST/rest/register.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      If StrExtract(goParam\Rcvd,~"\"validate\":\"",#DQUOTE$) = "email"
         SendeMsg(goParam)
      Else
         SendTextMsg(goParam)
      EndIf
      lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\"}"
   ElseIf goParam\Method+goParam\URL = "POST/rest/register_post.fusion?"+LCase(goParam\SiteID)
      WriteToLog(goParam\SiteID+" "+goParam\Rcvd)
      goParam\CCAcctNo = Trim(StrExtract(goParam\Rcvd,#dCCCAcctNo,","))
      goParam\HostKey1 = Trim(StrExtract(goParam\Rcvd,#dCHostKey1,","))
      goParam\HostKey2 = Trim(StrExtract(goParam\Rcvd,#dCHostKey2,","))
      goParam\CCNo = Trim(StrExtract(goParam\Rcvd,#dCCCNo,","))
      goParam\CCExpiry = Trim(StrExtract(goParam\Rcvd,#dCCCExpiry,","))
      goParam\CCName = Trim(StrExtract(goParam\Rcvd,#dCCCName,","))
      goParam\CCCVV = Trim(StrExtract(goParam\Rcvd,#dCCCVV,","))
      CC_CardVerify(@goParam)
      
      lcJSON = ~"{\"msg\":\""+goParam\Msg+~"\",\"cctoken\":\""+goParam\CCToken+~"\",\"fuelcard\":\""+goParam\FuelCard+~"\"}"
   Else
      WriteToLog("**ERROR Bad Request("+goParam\Method+goParam\URL+")")
      PokeS(*Status, "400 Bad Request", 13, #PB_Ascii)      
      Header\cchStatus = 15
      lcJSON = ~"{\"msg\":\"Bad Request - "+goParam\Method+goParam\URL+~"\"}"
   EndIf   
   *pECB\ServerSupportFunction(*pECB\ConnID, #HSE_REQ_SEND_RESPONSE_HEADER_EX, @Header, 0, 0)
   
   ;
   ; Send the html data with the WriteClient() callback:
   ;
   lnLen = StringByteLength(lcJSON,#PB_Ascii)
   *Ascii = Ascii(lcJSON)
   *pECB\WriteClient(*pECB\ConnID,*Ascii, @lnLen, 0)
   FreeMemory(*Ascii)
   FreeMemory(*Header)
   FreeMemory(*Status)
   If #dCTestMode And lnLen <= 2048
      WriteToLog(lcJSON)   
   ElseIf #dCTestMode
      WriteToLog(Left(lcJSON,2048)+"...")   
   EndIf
   If IsDatabase(goParam\dBNo)
      CloseDatabase(goParam\dBNo)
   EndIf
   ;
   ; Check log size once a day and clear if appropriate
   ;
   If goParam\Day <> Day(Date())
      goParam\Day = Day(Date())
      ClearLog()
   EndIf
   ProcedureReturn #HSE_STATUS_SUCCESS
EndProcedure
ProcedureDLL AttachProcess(Instance)
   OnErrorCall(@EHandler())
   goParam\Day = Day(Date())
   UseSHA2Fingerprint()
   UsePostgreSQLDatabase()
   SetCurrentDirectory(GetPathPart(ProgramFilename()))
   goParam\StartIn = GetCurrentDirectory()   
   
   ClearLog()
   If Not OpenPreferences(goParam\Startin+"dcMain.ini") Or Not PreferenceGroup("DATA")
      WriteToLog("**ERROR '[Data]' section missing in dcMain.ini")        
      RaiseError(#ERROR_DLL_INIT_FAILED)
   Else      
     If ExaminePreferenceKeys()
         While NextPreferenceKey()
            If PreferenceKeyName()="CONNECTIONSTRING"
               goParam\dBCStr = PreferenceKeyValue()
               Break
            EndIf
         Wend
      EndIf
      ClosePreferences() 
      goParam\dBName = "host=127.0.0.1 port="+StrExtract(goParam\dBCStr,"PORT=",";")+" dbname="+StrExtract(goParam\dBCStr,"DATABASE=",";")
      goParam\dBPwd = StrExtract(goParam\dBCStr,"PWD=",";")
      goParam\dBUser = StrExtract(goParam\dBCStr,"UID=",";")
   EndIf
   
   WriteToLog("AttachProcess("+goParam\StartIn+"dCFusionREST.dll ver. "+FormatDate("%yyyy.%mm.%dd.%hh.%ii",#PB_Compiler_Date)+" loaded successfully.)")
EndProcedure
Simon White
dCipher Computing
Post Reply