[sqlalchemy] Re: Caching

2007-12-22 Thread Anton V. Belyaev

 merge is working rudimentally for objects with unloaded scalar/
 instance/collection attributes in r3974.  whats not yet happening is
 the merging of the various query.options() that may be present on the
 original deferred loader, which means the merged instance wont
 necessarily maintain the exact eager/lazy/deferred loading of the
 original, but this is not especially critical for the basic idea to
 work.

 example script using merge attached.

Michael, thanks a lot for your support!
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Caching

2007-12-21 Thread Anton V. Belyaev

 I rearranged instance-level deferred loaders to be serializable
 instances in r3968.  you can now pickle an instance + its _state and
 restore, and all deferred/lazy loaders will be restored as well.  I
 didnt yet test it specifically with merge() but give it a try, you
 shoudnt be getting that error anymore...the pickling issue from ticket
 #870 is also no longer present.

Unfortunately it does not work (I am now at r3973).

1) I created an object with deferred property (not None).
2) Reloaded it in a new session (to erase deferred property)
3) Pickled/Unpickled
4) Removed everything but properties and _state.
5) obj = s.merge(obj, dont_load=True)  (with a fresh session s)
6) obj.deferred_ppty = None

merge worked without an exception this time.

Thanks.

PS. Special thanks for #871 (overheads in backref). It was blocking
the full-featured use of SqlAlchemy while staying as efficient as raw
SQL for me :)
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Caching

2007-12-20 Thread Anton V. Belyaev

Mike, thanks for your reply.

 what happens if you just leave _state alone ?  there shouldnt be any
 need to mess with _state (nor _entity_name).   the only attribute
 worth deleting for the cache operation is _sa_session_id so that the
 instance isnt associated with any particular session when it gets
 cached.  Id also consider using session.merge(dont_load=True) which is
 designed for use with caches (and also watch out for that log.debug(),
 debug() calls using the standard logging module are notoriously slow).

The reason for deleting _state is to save some space in cache. I save
instances to cache on get operation, so they are unmodified. But, of
course, it is internal thing so the final decision is yours :)

I gave up trying merge(dont_load=True) after running this sample:

users = Table('users', metadata,
  Column('id', Integer, primary_key=True),
  Column('name', String(100)),
  Column('surname', String(100)))

mapper(User, users,
   properties={
'surname': deferred(users.c.surname)
})

s = create_session()
u = User()
u.name = 'anton'
u.surname = 'belyaev'
s.save(u)
s.flush()

# now we need an instance with not loaded surname (because it is
deferred)
s = create_session()
u = s.query(User).get(1)

# cache it
cache = pickle.dumps(u)

# try to restore in a new session
s = create_session()
u = pickle.loads(cache)
u = s.merge(u, dont_load=True)

The latest statement fails with:

File /home/anton/eggs/lib/python2.5/site-packages/SQLAlchemy-0.4.1-
py2.5.egg/sqlalchemy/orm/session.py, line 1136, in object_session
if obj in sess:
TypeError: argument of type 'NoneType' is not iterable

Some notes on this test case:
1) If surname was a simple (not deferred) column property, merge
would work fine.
2) session.update(u) instead of merge would work fine even with
deferred column property, and the property itself would work fine (it
would load on first reference).

 the only trac ticket for this is #490, which with our current
 extension architecture is pretty easy to fix so its resolved in 3967 -
 MapperExtensions are now fully inherited.  If you apply the same
 MapperExtension explicitly to a base mapper and a subclass mapper,
 using the same ME instance will have the effect of it being applied
 only once (and using two different ME instances will have the effect
 of both being applied to the subclass separately).

I meant #870 (sorry, I should had provided reference in the first
message).

Back again to the testcase and note 2:

If, let say, I had some inheritance:

class Teacher(User):
pass

with polymorphic_fetch='deferred' (this is important), even
session.update(u) would not work. Because in this case deferred
attributes work through callables in _state, and callable does not
survive pickling.

Thanks.
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Caching

2007-12-20 Thread Anton V. Belyaev

 pickle isnt going to work with deferred columns unless you implement
 __getstate__ and __setstate__.  so the issue with session.merge() is
 just an extension of that issue, correct ?  i.e. without deferreds
 merge has no issue.

 is it not reasonable to ask that objects which are to be serialized
 and cached not have any deferred columns ? (or they are explicitly
 loaded before caching )?

Sorry, I dont understand clearly.

I do understand that pickle saves only __dict__ when no __getstate__
defined.

So, to be cached, an object should fetch all its deferred columns (if
any) and provide all of them at __getstate__. Right?

And if an instance from cache has nothing for one of its deferred
column values, then referencing these properties after merge wont load
them from DB, but just fail?

Thanks.
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Caching

2007-12-19 Thread Anton V. Belyaev

Hello,

Several people already wrote something about memcached + SqlAlchemy.

Remember, Mike Nelson wrote a mapper extention, it is available at:
http://www.ajaxlive.com/repo/mcmapper.py
http://www.ajaxlive.com/repo/mcache.py

I've rewritten it a bit to fit 0.4 release of SA.

Any response and comments are welcome, since I am not sure I am doing
right things in the code :) I dont like that dirty tricks with
deleting _state, etc. Maybe it could be done better?

But it works somehow. It manages to cache query get operations.

It has some problems with deferred fetch on inherited mapper because
of some issues of SA (I've found them in Trac).

import memcache as mc

class MCachedMapper(MapperExtension):
def get(self, query, ident, *args, **kwargs):
key = query.mapper.identity_key_from_primary_key(ident)
obj = query.session.identity_map.get(key)
if not obj:
mkey = gen_cache_key(key)
log.debug(Checking cache for %s, mkey)
obj = mc.get(mkey)
if obj is not None:
obj.__dict__[_state] = InstanceState(obj)
obj.__dict__[_entity_name] = None
log.debug(Found in cache for %s : %s, mkey, obj)
query.session.update(obj)
else:
obj = query._get(key, ident, **kwargs)
if obj is None:
return None
_state = obj._state
del obj.__dict__[_state]
del obj.__dict__[_entity_name]
mc.set(mkey, obj)
obj.__dict__[_state] = _state
obj.__dict__[_entity_name] = None
return obj

def before_update(self, mapper, connection, instance):
mkey =
gen_cache_key(mapper.identity_key_from_instance(instance))
log.debug(Clearing cache for %s because of update, mkey)
mc.delete(mkey)
return EXT_PASS

def before_delete(self, mapper, connection, instance):
mkey =
gen_cache_key(mapper.identity_key_from_instance(instance))
log.debug(Clearing cache for %s because of delete, mkey)
mc.delete(mkey)
return EXT_PASS

The mapper can be used like this:

mapper(User, users_table, extension=MCachedMapper())
session = create_session()
user_1234 = session.query(User).get(1234) # this one loads from the DB
session.clear()
user_1234 = session.query(User).get(1234) # this one fetches from
Memcached

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: SA and MySQL replication.

2007-12-09 Thread Anton V. Belyaev

On Dec 6, 11:51 pm, Andrew Stromnov [EMAIL PROTECTED] wrote:
 I have DB with onemasterserver and several replicated slaves
 (MySQL). How to implement this functionality: read-only request can be
 passed to any DB (masterorslave), but any write request with
 following read requests must be sended tomasteronly (to avoid
 synchronization lag).

This is an example of vertical partitioning. I am trying to find out
how to implement this with SA too.

Quite an offen-used scheme. Strange that no one has replied this
thread yet.

SA even has support for sharding (which is more complex than vertical
partitioning IMHO) so there certainly should be the way for 1-master-N-
slaves scheme.
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Unnecessary selects when cascade=all, delete-orphane and composite keys

2007-11-19 Thread Anton V. Belyaev

  I am building a grading system for students and got unexpected
  performance problems. I am using composite key for marks, which refer
  to students and subjects.

  When I am creating a mark (for student_1 and subject_1), unnecessary
  select operations are performed (select all marks for student_1 and
  select all marks for subject_1).

  Why these selects are generated and how to avoid them?

 The SQL issue looks like the marks collections on Student and
 Subject issuing a lazyload for their full collection of Mark items
 before the backref event appends the Mark object to each of them, i.e.
 the event that occurs when you issue mark.student = subject_1.
 Ordinary collections currently don't handle being present in a
 partial state, so in order for an append to occur, they load their
 contents.

 As a workaround, you can use lazy=dynamic relations for the
 collections, which is a special relation that can handle append
 operations without the full collection being available.
 lazy=noload would work as well but then you couldn't read from your
 collections.

 A future release may look into merging some of the dynamic relation
 behavior into an ordinary un-loaded collection so that this workaround
 would not be needed. Actually this might not be a bad idea for 0.4.2,
 so ive added ticket #871.

Thanks a lot for your reply!

Setting the relation to be lazy=dynamic really eliminated
unnecessary selects when creating Mark. Making a default relation a
bit dynamic is a great idea!

There is problem when relation is both lazy=dynamic and
cascade=all, delete-orphan:
When parent (Subject or Student) object is deleted, its children
(Marks) are not deleted. When relation is not dynamic, children are
deleted correctly.

Is this a proper behavior?

Thanks for the attention.

P.S. Congratulations to developers and users of SA with version 0.4.1!
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Unnecessary selects when cascade=all, delete-orphane and composite keys

2007-11-18 Thread Anton V. Belyaev

Hello all,

I am building a grading system for students and got unexpected
performance problems. I am using composite key for marks, which refer
to students and subjects.

When I am creating a mark (for student_1 and subject_1), unnecessary
select operations are performed (select all marks for student_1 and
select all marks for subject_1).

Why these selects are generated and how to avoid them?

Thanks for the attention.

Here is the sample code:

mapper(Student, table_students, properties={
'marks': relation(Mark, cascade='all, delete-orphan',
backref='student')
})
mapper(Subject, table_subjects, properties={
'marks': relation(Mark, cascade='all, delete-orphan',
backref='subject')
})
mapper(Mark, table_marks)

[... skip ...]

mark = Mark()
s.save(mark)
mark.student = student_1
mark.subject = subject_1
mark.value = 5
s.flush()

Here is SQL output (shortened a bit for ease of reading):

SELECT * FROM marks WHERE 1 = marks.student_id-
unnecessary select
SELECT * FROM marks WHERE 1 = marks.subject_id -
unnecessary select
BEGIN
INSERT INTO marks (subject_id, student_id, value) VALUES (1, 1, 5)
COMMIT

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] SA does implicit cascading

2007-09-03 Thread Anton V. Belyaev

Hello, here is a sample:

children_table = Table('children', metadata,
   Column('id', Integer, primary_key=True))
child2group_table = Table('child2group', metadata,
  Column('child_id', Integer,
ForeignKey('children.id'), nullable=False),
  Column('group_id', Integer,
ForeignKey('groups.id'), nullable=False))
groups_table = Table('groups', metadata,
 Column('id', Integer, primary_key=True))

mapper(Child, children_table,
   properties={ 'groups':relation(Group,
secondary=child2group_table) })

mapper(Group, groups_table, properties={
'children':relation(Child, secondary=child2group_table) })

Speaking English, this means there are groups and children, and child
can belong to some groups.

When I issue this:

group = session.query(Group).get(2)
session.delete(group)
session.flush()

SA does this:

2007-09-03 11:40:25,915 INFO sqlalchemy.engine.base.Engine.0x..8c
BEGIN
2007-09-03 11:40:25,918 INFO sqlalchemy.engine.base.Engine.0x..8c
DELETE FROM child2group WHERE child2group.child_id = ? AND
child2group.group_id = ?
2007-09-03 11:40:25,918 INFO sqlalchemy.engine.base.Engine.0x..8c [[3,
2], [4, 2]]
2007-09-03 11:40:25,919 INFO sqlalchemy.engine.base.Engine.0x..8c
DELETE FROM groups WHERE groups.id = ?
2007-09-03 11:40:25,920 INFO sqlalchemy.engine.base.Engine.0x..8c [2]
2007-09-03 11:40:25,921 INFO sqlalchemy.engine.base.Engine.0x..8c
COMMIT

Well, I dont mind, because this is what I really wanted to have.
Please, explain, why does this happen? I thought only group item would
be deleted and I would get orphaned records in child2group table.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Consistency with DB while modifying metadata

2007-07-25 Thread Anton V. Belyaev

 t = Table('mytable', meta,
 Column(...)
 
 )

 someothermeta = MetaData()
 t2 = Table('mytable', someothermetadata, autoload=True,
 autoload_with=connection)

 assert t.compare(t2)

I believe this should be done somehow automatically. Because everyone
needs this. There should be two separate disjoint options:

1) Autoload, when working with previously created database for rapid
start. Columns arent specified at all in Python code (SQLAlchemy).
2) Tables are specified in the code. Database tables might already
exist and might not. And when issuing create_all(), all the situations
should be handled correctly:
2a) If tables exist in DB and match Python-defined, ok.
2b) If tables do not exist in DB they are created, ok.
2c) If tables exist in DB and there is mismatch with Python-defined,
an exception is raised.

If feel this is kind of natural. Though, I am not an expert in DB or
SQLAlchemy.

 but why not just use autoload=True across the board in the first
 place and eliminate the chance of any errors ?

1) I dont know if tables exist. I might need to create them.
2) When they exist, autoloading them might cause inconsistency with
SQLAlchemy-defined tables in sources. This is exactly what I am trying
to avoid.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Consistency with DB while modifying metadata

2007-07-24 Thread Anton V. Belyaev

Hey,

I believe there is a common approach to the situation, but I just dont
know it.

Let say, I have some tables created in the DB using SQLAlchemy. Then I
modify Python code, which describes the table (add a column, remove
another column,...). What is the common way to handle this situation?
I guess it would be good to have an exception raised when there is a
mismatch between DB tables and Python-defined (using SQLAlchemy).


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Consistency with DB while modifying metadata

2007-07-24 Thread Anton V. Belyaev



On 24 июл, 17:34, svilen [EMAIL PROTECTED] wrote:
 On Tuesday 24 July 2007 16:22:43 Anton V. Belyaev wrote:

  Hey,

  I believe there is a common approach to the situation, but I just
  dont know it.

  Let say, I have some tables created in the DB using SQLAlchemy.
  Then I modify Python code, which describes the table (add a column,
  remove another column,...). What is the common way to handle this
  situation? I guess it would be good to have an exception raised
  when there is a mismatch between DB tables and Python-defined
  (using SQLAlchemy).

 Very soon i'll be in your situation (with hundreds of tables), so i'm
 very interested if something comes up.

 it's in the todo list of dbcook. my idea so far is:
  - automaticaly reverse engineer i.e. autoload the available
 db-structure into some metadata.
  - create another metadata as of current code
  - compare the 2 metadatas, and based on some rules - ??? -
 alter/migrate the DB into the new shape.
 This has to be as automatic as possible, leaving only certain - if
 any - decisions to the user.
 Assuming that the main decision - to upgrade or not to upgrade - is
 taken positive, and any locks etc explicit access is obtained.

 svil

Of course db modification is hard. It cant be done completely
automatically. For now I would like SQLAlchemy just to signal somehow
when its definitions are different from already existing db tables.
When I do create_all() it checks anyway tables properties, but doesnt
let me know when there is mismatch.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---