From: Tom Sawyer Date: 2002-08-28T15:02:18+09:00 Subject: Re: Ruby Based App Server may i offer a suggestion for the communication protocol for this promising App Server you all are cooking up? i recently needed to create an mini-app server myself for my accounting application in order to make it a 3-tier system. so i created ONI or Object Network Interface (very soon to be put on the RAA). the best part is that it uses YAML for message headers and serialization of objects. it works fantastically! it just needs to be beefed up. would you all be interested in using it in your pursuits? (i also would like to get it using the philosopy of REST if possible) ~tom p.s. BTW, here's that DB pool code i was talking about before: --=-=-= Content-Type: application/octet-stream Content-Disposition: attachment; filename=dbasePool.rb require 'config' require 'dbi' require 'thread' =begin =end class DbasePool @@classMutex = Mutex.new @@connectionFreed = ConditionVariable.new @@instance = nil @monitorThread @pool @poolSize @monitorThread attr_reader :host, :dbaseName attr_reader :userName, :password, :monitorInterval def initialize(host = nil, dbaseName = nil, userName = nil, password = nil, poolSize = 5, monitorInterval = 3600) if @@instance then raise "Attempted to re-init DbasePool" end @@classMutex.synchronize { @host = host @dbaseName = dbaseName @userName = userName @password = password @poolSize = poolSize @monitorInterval = monitorInterval #in seconds, 0 to disable monitoring. @pool = [] 0.upto(@poolSize-1) do |i| conn = @pool[i] = connect class << conn attr_accessor :status attr_accessor :parent def freeConnection parent.freeConnection(self) end #Now override several DBI's methods. #The default methods suck big time. #Also, the MySQL DBD driver does not #reflect true capability of MySQL. #So, the following changes allows for nested transaction, #and also provide transaction for any database that support it #regardless of what the DBD driver may say. attr_accessor :transaction_depth def begin_transaction if @transaction_depth == 0 self.do("BEGIN") puts "DBASE_POOL: Transaction started" end @transaction_depth += 1 end def commit_transaction if @transaction_depth == 1 self.do("COMMIT") puts "DBASE_POOL: Transaction stopped" end @transaction_depth -= 1 end def rollback_transaction if @transaction_depth == 1 self.do("ROLLBACK") puts "DBASE_POOL: Transaction rolled back" end @transaction_depth -= 1 end def transaction raise InterfaceError, "Database connection was already closed!" if @handle.nil? raise InterfaceError, "No block given" unless block_given? begin_transaction begin yield self commit_transaction rescue Exception rollback_transaction raise end end end conn.status = "IDLE" conn.parent = self conn.transaction_depth = 0 end @monitorThread = Thread.new do while(true) sleep(monitorInterval) @@classMutex.synchronize { #puts "HAI, monitoring is awake" @pool.each do |conn| if (conn.status == "IDLE") && (not conn.ping) conn = connect end end } end end @@instance = self } end def connect DBI.connect("DBI:Mysql:#{@dbaseName}:#{@host}", @userName, @password) end #What's the destructor name in Ruby? def finish @@classMutex.synchronize { @monitorThread.stop @pool.each do |conn| conn.disconnect end } end #Get the next idle connection. If there is no idle connection, will block #until there is one. #Optionally accepts a block. If a block is given, will execute the block #and free up the connection afterwards. def getConnection idleConn = nil #predeclare @@classMutex.synchronize { callcc do |cont| idleConn = @pool.find do |conn| conn.status == "IDLE" end if not idleConn puts "No more free connection in DBASE_POOL. Waiting..." puts @@instance.inspect puts caller.join("\n") @@connectionFreed.wait(@@classMutex) puts "DBASE_POOL: a connection is freed. Resuming." cont.call end end idleConn.status = "BUSY" } if block_given? yield idleConn idleConn.freeConnection else idleConn end end def freeConnection(usedConn) conn = nil #predeclare @@classMutex.synchronize { conn = @pool.find do |conn| conn == usedConn end } if conn conn.status = "IDLE" @@connectionFreed.signal else raise "Cannot found the referred connection!" end end end # Initialise DBASE_POOL DBASE_POOL = DbasePool.new(Config::DBASE_HOST, Config::DBASE_DBNAME, Config::DBASE_USER, Config::DBASE_PASSWORD) if $0 == __FILE__ #test, and also benchmark. #on AMD-K62 400MHz, can do 217 SELECT NOW() statements per second. 1.upto(1000) do |i| puts i if i % 100 == 0 dbh = [] 1.upto(5) do |i| dbh[i] = DBASE_POOL.getConnection end 1.upto(5) do |i| dbh[i].execute("SELECT NOW()") end 1.upto(5) do |i| DBASE_POOL.freeConnection dbh[i] end 1.upto(5) do DBASE_POOL.getConnection do |dbh| dbh.execute("SELECT NOW()") end end end end --=-=-=-- <^> On Tue, 2002-08-27 at 23:43, Edward Wilson wrote: > > What does the Ruby community think? > > I have wrestled for 6 months with a idea of writing somthing like this > in C++ and embedding Ruby into a C++ shell of rich objects. > Ultimately I would extend the server using both C/C++ and Ruby. > > I have recently come to the conclusion that writing such a beast in > pure Ruby is the only way. Writing an app server covers so much > ground. The implementation could remain a prototype for three years > if it were written in C++. Ruby would be the only way to get off the > ground in less than a year. Porting would be much easier if the > server were written in pure Ruby. > > Application servers are mini-operating systems in their own right. > > iPlanet was a disaster; JBoss, BEA, JRun, and Tomcat are fantastic > implementations. Even Zope/Python has made a run. > > > //ed > -- ~transami