OODB Storage Options and Performance

Daniel Salama dsalama at user.net
Tue Apr 12 18:58:16 UTC 2005


It's me again :)

I'm not here to put down any storage option available. I just wanted to  
share with you guys another little experiment I did with GOODS,  
OmniBase, Magma, MySQL, and MySQL with RubyOnRails.

The test consisted of an extremely simplistic process of updating a  
sequence number in a dictionary. In a typical CRM-like application, I  
would use something like this in order to "generate" a customer ID  
number for new customers (unless someone has a better suggestion). In  
essence, the test updates a single dictionary element (a counter) 1000  
times. I'm sure the code could be optimized so I'm open to suggestions  
on how to do so.

My intentions here were not really to compare an OODB against a RDB.  
You may decide to ignore the MySQL results.

The GOODS code looked like this:

time:= Time millisecondsToRun:
[Transcript cr..
db := KKDatabase onHost: 'localhost' port: 6100.
db root at: 'Sequences' put: (Dictionary new).
db commit.
base := db root at: 'Sequences'.
nextCustomerNo := base at: 'CustomerNo' ifAbsent: [0].
[0 to: 999 do:
	[:i|
	(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
	nextCustomerNo := nextCustomerNo + 1.
	base at: 'CustomerNo' put: nextCustomerNo.
	db commit]] ensure: [db logout]].

Transcript cr; show: (time/1000) asFloat; show: ' seconds'.

db := KKDatabase onHost: 'localhost' port: 6100.
base := db root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:  
'CustomerNo').
db logout.

------------------------------------------------------------------------ 
------------

The OmniBase code looked like this:

time:= Time millisecondsToRun:
[Transcript cr..
db := OmniBase openOn: 'Macintosh HD:Users:dsalama:OB'.
[OmniBase root at: 'Sequences' put: (Dictionary new)]
	evaluateAndCommitIn: db newTransaction.
[base := OmniBase root at: 'Sequences'.
base at: 'CustomerNo' put: 0.
base markDirty.
] evaluateAndCommitIn: db newTransaction.
[0 to: 999 do:
	[:i|
	(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
	[base := OmniBase root at: 'Sequences'.
	nextCustomerNo := (base at: 'CustomerNo') + 1.
	base at: 'CustomerNo' put: nextCustomerNo.
	base markDirty] evaluateAndCommitIn: db newTransaction.
	]] ensure: [db close]].

Transcript cr; show: (time/1000) asFloat; show: ' seconds'.

db := OmniBase openOn: 'Macintosh HD:Users:dsalama:OB'.
base := (db newTransaction) root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:  
'CustomerNo').
db close.

------------------------------------------------------------------------ 
------------

The Magma code looked like this:

time:= Time millisecondsToRun:
[Transcript cr..
db := MagmaSession hostAddress: #(127 0 0 1) asByteArray port: 51969.
db connectAs: 'dsalama'.
db commit:
	[db root at: 'Sequences' put: (Dictionary new)].
base := db root at: 'Sequences'.
db commit:
	[base at: 'CustomerNo' put: 0].
[0 to: 999 do:
	[:i|
	(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
	base := db root at: 'Sequences'.
	nextCustomerNo := (base at: 'CustomerNo') + 1.
	db commit: [base at: 'CustomerNo' put: nextCustomerNo].
	]] ensure: [db disconnect]].

Transcript cr; show: (time/1000) asFloat; show: ' seconds'.

db := MagmaSession hostAddress: #(127 0 0 1) asByteArray port: 51969.
db connectAs: 'dsalama'.
base := db root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:  
'CustomerNo').
db disconnect.

------------------------------------------------------------------------ 
------------

The MySQL code looked like this:

time:= Time millisecondsToRun:
[Transcript cr..
Socket initializeNetwork.
spec := (JdmConnectionSpec new initialize
	user: 'master'; password: 'secret';
	host: (NetNameResolver addressForName: 'localhost');
	database: 'dcm'; port: 3306).
connection := JdmConnection on: spec.
statement := connection createStatement.
resultSet := connection createStatement executeQuery: 'insert into  
tests values (null, ''CustomerNo'', 0)'.
0 to: 999 do:
	[:i|
	(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
	connection := JdmConnection on: spec.
	resultSet := connection createStatement executeQuery: 'select next  
from tests where sequence = ''CustomerNo'''.
	resultSet next.
	nextCustomerNo := (resultSet valueNamed: 'next') + 1.
	connection close.
	[connection := JdmConnection on: spec.
	updateSQL := 'update tests set next = ', (nextCustomerNo asString), '  
where id = 1'.
	connection createStatement executeQuery: updateSQL]
		ensure: [connection close]]].

Transcript cr; show: (time/1000) asFloat; show: ' seconds'.

connection := JdmConnection on: spec.
statement := connection createStatement.
resultSet := statement executeQuery: 'select next from tests where  
sequence = ''CustomerNo'''.
resultSet next.
nextCustomerNo := (resultSet valueNamed: 'next').
Transcript cr; show: 'Last Customer No: '; show: nextCustomerNo.
connection close.

Granted, that after Alan and Boris explained to me the "limitation" of  
the MySQL driver, there is additional overhead in opening and closing  
the connection 1000 times.

------------------------------------------------------------------------ 
------------

And the RubyOnRails code looked like this:

require 'test'

t0 = Time.now
0.upto(999) do |i|
         puts "." if (i % 100) == 0
         t = Test.find_by_sequence('CustomerNo')
         unless t
                 t = Test.new
                 t.sequence = 'CustomerNo'
                 t.next = 0
         end
         t.next += 1
         t.save
end
t1 = Time.now
puts (t1-t0).to_s + ' seconds'
puts "Last Customer No: " +  
Test.find_by_sequence('CustomerNo').next.to_s

------------------------------------------------------------------------ 
------------

The tests were performed on a Powerbook G4 1.5GHz with 1GB RAM with  
Squeak 3.7-5989-full with the latest(?) versions of the respective  
class libraries.

GOODS: 201.553 seconds
Omnibase: 3.102 seconds
Magma: 73.578 seconds
MySQL: 13.815 seconds
RubyOnRails: 12.411535 seconds

Interestingly the MySQL results were not much better (or worse),  
whether you use Squeak or RubyOnRails. It's simply a point of  
comparison for the other tests.

I was definitely impressed with OmniBase's performance. As I had said  
in previous postings, OB has provided me the best performance so far  
during my tests and every day I feel more confident in OB. Also, as I  
mentioned in the past, I am now getting ready to purchase a commercial  
license for it and hope to get the Linux file locking support from Avi  
as well. Also, I hope my move towards purchasing a license motivates  
others (e.g. Cees de Groot) to help David support and maintain OB. I  
also wish there was better documentation for it.

I was a bit disappointed at the performance of GOODS. I like the GOODS  
server and the people I have talked with regarding its performance  
under "heavier" loads are very happy with it. Again, as I mentioned in  
previous postings, they are not using Smalltalk. They are using Java or  
C. As Avi said, it could be a performance tuning issue with the Squeak  
GOODS classes and hopefully that would improve over time.

Now, I hope my findings are useful to others. However, I would love to  
hear feedback from others regarding this. I tried to make these as  
equal and unbiased as possible. Also, the fact that I'm new to all this  
may affect the quality and optimization of my code.

Thanks,
Daniel


Daniel Salama
dsalama at user.net
Voice: (954) 655-8051
Fax  : (954) 252-3988

------------------------

This e-mail contains information which may be confidential and
privileged. Unless you are the addressee (or authorized to
receive for the addressee), you may not use, copy or disclose
to anyone the message or any information contained in the
message.  If you have received the message in error, please
advise the sender by reply e-mail to dsalama at user.net or
tel. +1-954-655-8051 and delete the material from any computer.




More information about the Squeak-dev mailing list