I'm using Postmodern to query and update a table in a database. The application is networked and threaded, and in the past we've had issues where we get compilation style warnings at run time, and even errors which pop up the debugger. These issues arose when we began flowing client traffic to our application, and I thought that they might be because of some race condition in Postmodern's internals when it starts doing MOP operations on our class which has had (:metaclass dao-class) added to its class definition.
Because I suspected a race condition--that multiple threads might be trying to instantiate the MOP stuff around our database accessor class at once--I worked around it by having our startup procedure do a single database access before allowing any client traffic in. That seemed to help things for a while, but this morning I was using the profiler to instrument our code. When I ran the program, and at a point where only that one retrieval should've been running, it popped up with an error in the debugger.
I'm using postmodern-20120520-git as loaded through Quicklisp, on SBCL 1.0.57. The issues have arisen on a variety of x86 hardware, both 32- and 64-bits.
Here's the defclass for the table in question:
(defclass device () ((device-uid :col-type integer :initarg :device-uid :accessor device-uid) (macaddress :col-type string :accessor device-macaddress) (status :col-type string :accessor device-status) (state :col-type string :initarg :state :accessor device-state) (nature :col-type string :accessor device-nature) (client-uid :col-type integer :accessor device-client-uid) (user-uid :col-type integer :accessor device-user-uid)) (:metaclass dao-class) (:keys device-uid))
and here's the function which we call to prime the pump at the start of program execution:
(defun get-device-object-by-device-uid (device-uid) (with-connection *db-parameters* (log-message :device-dao-bug "In get-device-object-by-device-uid, device-uid = ~A" device-uid) (let ((result (query (:select '* :from 'device :where (:= 'device-uid device-uid)) (:dao device)))) (log-message :device-dao-bug "Finished query in get-device-object-by-device-uid.") (if result (car result) nil))))
The error I got this morning was:
POSTMODERN:DAO-EXISTS-P already names an ordinary function or a macro. [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
Restarts: 0: [CONTINUE] Replace the function binding 1: [*ABORT] Return to SLIME's top level. 2: [ABORT] Abort thread (#<THREAD "repl-thread" RUNNING {1003E48113}>)
Backtrace: 0: (ENSURE-GENERIC-FUNCTION POSTMODERN:DAO-EXISTS-P) 1: (SB-PCL::REAL-ADD-NAMED-METHOD POSTMODERN:DAO-EXISTS-P NIL (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) (POSTMODERN::OBJECT) #<unavailable &REST argument>) 2: (SB-PCL::LOAD-DEFMETHOD-INTERNAL ..) 3: ((LAMBDA ())) 4: ((LAMBDA (SB-INT:&MORE SB-PROFILE::ARG-CONTEXT SB-PROFILE::ARG-COUNT) :IN SB-PROFILE::PROFILE-ENCAPSULATION-LAMBDAS) #<unavailable &MORE argument>) 5: ((LAMBDA (SB-INT:&MORE SB-PROFILE::ARG-CONTEXT SB-PROFILE::ARG-COUNT) :IN SB-PROFILE::PROFILE-ENCAPSULATION-LAMBDAS) #<unavailable &MORE argument>) 6: ((SB-PCL::EMF SB-MOP:FINALIZE-INHERITANCE) #<unavailable argument> #<unavailable argument> #<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) 7: (PROJECT-PACKAGENAME::GET-DEVICE-OBJECT-BY-DEVICE-UID 1)
Earlier, when we were allowing multiple threads to access this object at the same time, we got output like this (all of the following is output from one session):
debugger invoked on a UNBOUND-SLOT in thread #<THREAD "message handler2" RUNNING {1004843BE3}>: The slot POSTMODERN::COLUMN-MAP is unbound in the object #<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>. STYLE-WARNING: redefining POSTMODERN:DAO-EXISTS-P (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:UPDATE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:DELETE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:GET-DAO (#<SB-MOP:EQL-SPECIALIZER {100C80AFB3}>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:INSERT-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN::FETCH-DEFAULTS (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining SHARED-INITIALIZE :AFTER (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE> #<BUILT-IN-CLASS T>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:DAO-EXISTS-P (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:UPDATE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:DELETE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:GET-DAO (#<SB-MOP:EQL-SPECIALIZER {100C80AFB3}>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:INSERT-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN::FETCH-DEFAULTS (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining SHARED-INITIALIZE :AFTER (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE> #<BUILT-IN-CLASS T>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:DAO-EXISTS-P (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:UPDATE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:DELETE-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:GET-DAO (#<SB-MOP:EQL-SPECIALIZER {100C80AFB3}>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN:INSERT-DAO (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining POSTMODERN::FETCH-DEFAULTS (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE>) in DEFMETHOD STYLE-WARNING: redefining SHARED-INITIALIZE :AFTER (#<POSTMODERN:DAO-CLASS PROJECT-PACKAGENAME:DEVICE> #<BUILT-IN-CLASS T>) in DEFMETHOD
For the time being, we're planning to work around this by avoiding the use of the dao-class metaclass; we'll write our database queries to return lists or alists, and we'll write update operations directly as SQL or S-SQL queries instead of using update-dao. If there's a way to address this problem and continue using the dao-class metaclass, though, we'd rather do that.
Any advice would be most welcome.
Keith Browne