It seems obvious (and necessary) to have an object cache in your OPF. Before
creating a new object with a specific OID, you would check the cache to see
if that OID already exists.
My simple question is this: How do you determine if you can remove a
particular OID from the cache?
Obviously, over time the cache will grow large and it will be necessary to
remove objects from it. How can I be sure it is safe to remove the object?
Answers I've tossed about in my head:
1. Implement my own reference counting mechanism (blech!)
2. Interfaces (I don't know squat about them, but I think many of you are
going to recommend them)
3. This is managed .NET code. It would be freakin' nice to be able to query
the .NET garbage collector to see if the cache is the only reference to the
object.
I'll hang up and listen off the air.
"Scott Roberts" <scott....@no-spam-intelebill.com> wrote in message
news:40d0b479$1...@newsgroups.borland.com...
That being the case, have your objects check into a list based on weak
references. Basically, a WeakReference object holds an object pointer that
does not prevent garbage collection, and can return either a strong
reference or nil.
If not, then yes, an interface-based cache or ref-counting are your two
primary options. Both work, use whichever is compatible with the rest of
your system.
bobD
Hi,
> My simple question is this: How do you determine if you can remove a
> particular OID from the cache?
We have a couple of settings in our cache, MaxSize and MaxIdleTime.
If MaxSize of the cache is reached, the 'oldest' object is removed (that
is, the object that was last used the longest time ago). For
MaxIdleTime, the object is removed XX seconds after it was last used.
If MaxIdleTime is Zero, the object is removed as some as the transaction
has completed. If MaxIdleTime is -1, the object is never removed,
unless MaxSize is reached.
> Obviously, over time the cache will grow large and it will be necessary to
> remove objects from it. How can I be sure it is safe to remove the object?
To implement MaxSize and MaxIdleTime our cache has a 'HashList' that
contains a list of OID and a timestamp. That timestamp is updated to
'now' when a transaction ends that has used the object. (Hope that makes
sense?). If the cache maxsize has be reached and a new object needs to
be added the 'HashList' is sorted by date ascending, and the first
object removed. For the MaxIdleTime, there is a background thread that
runs every second the compares the timestamp to Now + MaxIdleTime, and
removes the object when appropriate.
Phil
--
Discover a lost art - play Marbles
May 2004
www.marillion.com
It is important to remark that I did not include the reference counting
in the object. I put it in the cache to avoid polluting the object.
To give you an Idea....
TOldCleanCache = class (TPersistenceCache)
private
FCachedInfos:TCachedObjectInformationCollection;
protected
property CachedInfos:TCachedObjectInformationCollection read
FCachedInfos write FCachedInfos;
public
constructor Create;override;
Function FindObject(ID:string; aClass:TClass):TObject;overload;override;
Function FindObject(anObj:TObject):TObject;overload;override;
Function HasObject (anObj:TObject):boolean;override;
Function RemoveObject(ID:string;
aClass:TClass):TObject;overload;override;
Function RemoveObject(anObj:TObject):TObject;overload;override;
procedure addObject(anObj:TObject);override;
procedure RunGarbageCollector(Sender:TObject);
procedure addReference(anObject:TObject);
procedure removeReference(anObject:TObject);
Destructor Destroy;override;
End;
and....
TCachedObjectInformation = class(TObject)
private
FCachedObject : TObject;
FMemoryTimeStamp : TDateTime;
FRefCount:integer;
procedure setRefCount(const Value: integer);
published
property CachedObject:TObject read FCachedObject write FCachedObject;
property MemoryTimeStamp:TDateTime read FMemoryTimeStamp write
FMemoryTimeStamp;
property RefCount:integer read FRefCount write setRefCount;
public
function OlderThan(iMinutes:integer):boolean;
end;
Note that implementing a Cache is not mandatory. I saw many good
frameworks programmed that didnt use a cache. One of the advantages of the
cache is performance but you have to pay more attention to concurrency.
Where is your cache? In every client or in a server application?.
A final note.It really depends in your design but from what I know if you
make your cache in .NET you won't be having problems using it from any .NET
language. But if you want to use it from Java or a Win32 application I think
that should impact in your design.
Esteban Calabria
TForm1.Button1Click;
Begin
Self.FClient := Framework.RecoverClient('Peter');
end;
TForm1.Button2Click
Begin
Self.FClient.Priority := 'High';
end;
What happens if users clicks Button1, goes to take lunch and two hous latter
it comes back and click button2?.
Wont the object be freed?
Esteban Calabria
"Phil Shrimpton" <ph...@nospam.co.uk> escribió en el mensaje
news:MPG.1b3ba5b2a...@forums.borland.com...
Clever. I like the concept. However, there are a couple of sticking points:
1. Assuming I get the weak references to work (see below), how do I
determine if the object referenced by the weak reference has been garbage
collected or not?
2. I am under the impression that in a garbage collected environment (such
as .NET) objects are not really reference counted. Instead, the heap is
inspected and the garbage collector magically knows when memory can no
longer be referenced by the application and can be garbage collected. This
means that circular references are not a problem (yeah!) but even a weak
pointer to a particular object would prevent it from being garbage collected
(boo!). The only way I can think of off the top of my head is to implement
the cache using unmanaged code (the gc evidently ignores references in
unmanaged code), but I would like to avoid that. I'll ask around in the .NET
groups about weak references and garbage collection.
Here's the problem. You're not required to "free" an object in .NET. You can
simply let the reference go out of scope and let the garbage collector clean
up after you. If I create my own reference counting scheme (I like yours)
then application programmers have to remember to free objects when they are
finished with them. While this seems easy enough for seasoned Delphi or C++
programmers who are used to handling memory management, these VB and .NET
kids coming out of college now don't seem to "get it". I'm not ruling this
out, it would just be nice to find a way around it.
> Note that implementing a Cache is not mandatory. I saw many good
> frameworks programmed that didnt use a cache. One of the advantages of the
> cache is performance but you have to pay more attention to concurrency.
> Where is your cache? In every client or in a server application?.
It will be a server application. Tell me more about not implementing a
cache. How do you prevent multiple instances of the same OID in memory
without a cache?
> A final note.It really depends in your design but from what I know if you
> make your cache in .NET you won't be having problems using it from any
.NET
> language. But if you want to use it from Java or a Win32 application I
think
> that should impact in your design.
We haven't strictly settled on a language yet, but it will be .NET or Java.
I believe Win32 = DOS in short order.
In .NET the object will not be freed. However, the object would have been
removed from the cache and if the user clicked button 3:
TForm1.Button1Click;
Begin
Self.FClient2 := Framework.RecoverClient('Peter');
end;
then you would have two distinct instances of the "Peter" object in memory,
which is what the cache should be preventing.
Hi,
> > If MaxSize of the cache is reached, the 'oldest' object is removed (that
> > is, the object that was last used the longest time ago). For
> > MaxIdleTime, the object is removed XX seconds after it was last used.
> > If MaxIdleTime is Zero, the object is removed as some as the transaction
> > has completed. If MaxIdleTime is -1, the object is never removed,
> > unless MaxSize is reached.
>
> Using time stamps alone seems dangerous. How can you be sure there are no
> references to the object before you remove it?
I missed that bit out <g>
When an object is first used in a transaction it is added to the
'transactions' object list, before an object is removed all the
transaction's object lists' are iterated to see if it is in use.
Hi,
> What if the following case arrises...
>
> TForm1.Button1Click;
> Begin
> Self.FClient := Framework.RecoverClient('Peter');
> end;
>
> TForm1.Button2Click
> Begin
> Self.FClient.Priority := 'High';
> end;
>
> What happens if users clicks Button1, goes to take lunch and two hous latter
> it comes back and click button2?.
> Wont the object be freed?
Depends on the transaction settings. The object won't be removed if it
is involved in a transaction, so if the transaction is still active when
the user comes back from lunch the object will still be their if the
transaction is still active. In reality our transactions normally have
a time out to stop this sort of thing, so the user would more liky get a
'transaction time out' error.
(Read more message does not end here...)
> simply let the reference go out of scope and let the garbage collector
clean
> up after you. If I create my own reference counting scheme (I like yours)
<REMOVED TEXT>
> > cache is performance but you have to pay more attention to concurrency.
> > Where is your cache? In every client or in a server application?.
>
IF you make a server application it would be nice to handle a cache. The
question is... how would the client acces the objecs in the server? Would
they use remote proxys instead of BO? Would they have a copy of the objects?
If you dont have a chache this is the sollution I saw in most cases: you can
have multiple instance of an object with the same OID in memory. The problem
comes when you update the content to the database. You hace to implement a
version controlling system or something like that.
If you try to update an object but someone else updated it first, you will
have an exception that says somethin like "Another user had modified
<object>. Please try again"
> It will be a server application. Tell me more about not implementing a
> cache. How do you prevent multiple instances of the same OID in memory
> without a cache?
>
> > A final note.It really depends in your design but from what I know if
you
> > make your cache in .NET you won't be having problems using it from any
> .NET
> > language. But if you want to use it from Java or a Win32 application I
> think
> > that should impact in your design.
>
> We haven't strictly settled on a language yet, but it will be .NET or
Java.
> I believe Win32 = DOS in short order.
>
>
If you want your OFP to work with different languages I thought of something
like this...
A server that holds your objecs programmed in any language you like...
The OPF automatically generates code. It generates remotes proxys for each
object you marked as persistent
The client application (Jave, .NET, c++, Delphi7 :) ) uses proxys. The BO in
memory only exists in the Server.
Actually I intend to do something like that in my OPF. But I havent designed
the next prototipe yet, so this just what I have in mind.
Esteban Calabria
(Just a thought: Maybe, to avoid having to instance of peter in
memory you can reinsert the object if it is not in the cache for every call
to any object setter/getter/method.)
Esteban Calabria
"Scott Roberts" <scott....@no-spam-intelebill.com> escribió en el
mensaje news:40d1abe3$1...@newsgroups.borland.com...
Not "weak references" in the sense of a using a pointer in Delphi to prevent
reference counting. WeakReference is a .NET class--have a look at it in the
help files.
Basic logic is
cache holds a list of objects containing information about class and ID,
and a weak reference to the object
a call for an instance
calls cache, searches list
found?
yes
ask weak reference for Target
if Target != nil then
return it (it's a strong reference now)
else
call constuctor, reassign Target property to new object
return strong reference
no
call constuctor
insert new weak reference holder into cache
return strong reference
> 2. I am under the impression that in a garbage collected environment
> (such as .NET) objects are not really reference counted. Instead, the
right so far.
> but even a weak pointer
A WeakReference is a .NET class, not just a type of pointer: it's a specific
object type that does not prevent what it holds from being collected.
Essentially, it's a class designed to do exactly what you need to implement
an opportunistic cache--maintain a reference to an item without interfering
with garbage collection.) The WeakReference class has an IsAlive property
you can check to see if the held object has been collected. Additionally,
the Target property returns either a strong reference or (if not IsAlive),
null.
bobD
Esteban Calabria
Hi,
> We haven't strictly settled on a language yet, but it will be .NET or Java.
If you are going the Java route, all the caching/transaction stuff is
built into the J2EE platform. Most of our Delphi OPF code is
essentially replicating what comes out the box with Java (or existing
Java products). If you don't want to go the J2EE route in Java, there
are a number of free, excellent, OPF frameworks that have this sort of
stuff built in an means it is all but a waste of time writing your own.
Bob Dawson for President! (assuming you are a U.S. citizen, of course)
"Bob Dawson" <bda...@idtdna.com> wrote in message
news:40d1...@newsgroups.borland.com...
Unfortunately, no. I believe the reason is that .NET doesn't actually use
reference counting. What would be really nice is to be able to register a
"listener" on the .NET garbage collector to have it inform you every time it
gc'ed something.
> IF you make a server application it would be nice to handle a cache. The
> question is... how would the client acces the objecs in the server? Would
> they use remote proxys instead of BO? Would they have a copy of the
objects?
I was originally thinking of .NET remoting. However, after perusing the MS
newsgroups briefly it seems that MS highly recommends XML WS over .NET
remoting for n-tier applications. In this case, the server can cache objects
for speed, but each client will have its own copy of the object.
> If you dont have a chache this is the sollution I saw in most cases: you
can
> have multiple instance of an object with the same OID in memory. The
problem
> comes when you update the content to the database. You hace to implement a
> version controlling system or something like that.
> If you try to update an object but someone else updated it first, you will
> have an exception that says somethin like "Another user had modified
> <object>. Please try again"
Our current application only saves changed fields. So if one user changes
the last name and another changes the first name then both changes will save
successfully. If both users change the last name, then "last save wins". We
are okay with this.
> If you want your OFP to work with different languages I thought of
something
> like this...
>
> A server that holds your objecs programmed in any language you like...
>
> The OPF automatically generates code. It generates remotes proxys for each
> object you marked as persistent
>
> The client application (Jave, .NET, c++, Delphi7 :) ) uses proxys. The BO
in
> memory only exists in the Server.
.NET remoting does this for you. However, I am afraid of the amout of
network traffic generated as well as having to cache that many objects on
the server. I'm also not certain I want to bet the farm on .NET just yet. At
this point, we are leaning strongly toward XML WS and using "disconnected"
objects in the middle (BO) tier. This should keep us pretty well language
(and to some extent platform) independant.
As for 3rd party OPFs, I'm just afraid of 3rd party code. Call me a scardey
cat (sp?), but when the performance sucks eggs or the app crashes and burns
3 times a day, *I* am the one responsible and *I* want to be able to fix the
problem. Plus, I kinda like reinventing wheels. Especially when there are a
lot of sucky wheels out there. ;)
nahh--just happened across it while playing around with how to convert my
own OPFa while back. It's mentioned in Richter's _Applied MS .NET Framework
Programming_.
> Bob Dawson for President! (assuming you are a U.S. citizen, of course)
Yeah, but child of the sixties--hopelessly unelectable...
bobD
Not *asking* for too much - but there may not exist one single answer to all
needs. That's a potential problem when designing OPFs - Trying too hard to
make the "total solution". Many full OPFs are written, still developers are
looking for *the* OPF. What I'm trying to say is: It's hard enough to make
an OPF. Don't believe that you may do it all in one round.
To your original question: Lifetime control is essential in an OPF. One
concept already exists in Delphi: The 'TComponent.Owner' - determining the
lifetime of a component. I'd say you have a few choices:
- Using interfaces and reference counting. This is beautiful when the "user"
is to be in control, and you *want* an object to be freed when noone is
referencing it. IMHO suitable for "root" objects & temporarily created
objects, but not for e.g. an Invoice line.
- The 'Owner' concept, where the owner frees all of its owned objects upon
destruction. This is by far the most flexible concept, IMHO, as you may
actually decide behaviour from case to case without altering the class in
question.
- Control it all in classes' code. This is the pragmatic one, being 100%
flexible but also leaving it all to be taken care of by the programmer for
each and every object she or he creates.
You'll experience that the lifetime control concept has a lot of impact on
the rest of the application code. Especially on "notification flow", which
is to my experience a central part of any OPF. In addition to get "safe
object destruction", you need to be able to notify upon data/state changes
to have controls refreshed, etc..
Before you buy the Interface based concept, concider the situation where an
object simply should be there as long as another one is there. Mutual
referencing needs to be worked around (not extremely hard), and this
situation is similar to not having any "automatic" handling at all - you
need to write lines of code to make it work like desired. When you know the
exact points in execution where objects should be freed, Interfaces may
solve nothing at all.
OTOH, take applications like Outlook Express, where you may operate on a
number of different messages at once without having a well defined "freeing
point": Interfaces may make it all beautiful. You have probably noticed that
there must be some application core that exists for as long as any screens
are active. If the core was an Interface and the object implemented
automatic lifetime control based on reference counting, you would have this
for free.
I have used reference counted objects for simply replacing the typical:
var
Obj: TInterfacedObject;
begin
Obj:=TInterfacedObject.Create;
try
// Do things
finally
Obj.Free;
end;
end;
...with:
var
Obj: IUnknown;
begin
Obj:=TInterfacedObject.Create;
// Do things
end;
...just for the simplicity of it. A simplification similar to using strings
instead of PChars. But yet I *still* use PChars from time to time ;-)
If you think about interfaces, concider how it's done in exec Excel/Word
automation...where (AFAIK) only the 'root' objects are reference counted. It
doesn't make sense to have *one worksheet* automatically disposed as long as
it is part of a workbook ...and it's all stored in one file.
--
Regards,
Bjørge Sæther
bjorge@haha_itte.no
-------------------------------------
I'll not spend any money on American Software products
until armed forces are out of Iraq.
If your server application is the only way of access to the data, you may of
course cache the object for later use. But for this to have any meaning,
objects must be either a) retrieved over and over again by many clients
within a short while, and/or b) have a consuming construction process.
Remember, both file system and DB server has a cache, too, so if you
retrieve a record twice within a short while, the second one would be
superfast. Are you sure you *need* an object cache ?
This is an interesting problem. In the "good old" C/S days (with "live"
datasets), this was not a possibility - as the second user would not be able
to modify a row being edited. The question is actually whether you *want*
two independent users to modify the same record or not ? With DB row
locking, if the answer was "yes", you'd have to write quite a few lines of
code to work around it. With "Offline" objects the default behaviour would
be "overwrite", and you need to write a mechanism to prevent this situation.
I must say I feel quite stupid trying to create a sulution to this for a
MIDAS application simply to handle a standard concurrency issue. Just
because it's "offline", and has its own strange way of dealing with these
conflicts.
In an OO app of mine I created a communicatiuons channel where all clients
were informing others about what objects were editing and what objects had
new values stored. To reduce number of conficts encountered upon "save". But
this was a scenario where two persons could accidentially work for half an
hour with the same object...just to have their work discarded upon 'save'.
This solution actually informed them that they *were* about to do an
undesired operation, and that they could shout to the other guy: "HEY !
THAT'S MY CUSTOMER !!!".
I find the comparison to 'wheel' a bit misleading when talking about OPFs.
It's more like with stereos: "Do we make one, integrated box, or do we
separate it into components?". Some OPFs have a mains wire and 2 connections
for speakers, while others have separate, trimable volume controls. Which
one is better ?
> This is an interesting problem. In the "good old" C/S days (with "live"
> datasets), this was not a possibility - as the second user would not be
able
> to modify a row being edited. The question is actually whether you *want*
> two independent users to modify the same record or not ? With DB row
> locking, if the answer was "yes", you'd have to write quite a few lines of
> code to work around it. With "Offline" objects the default behaviour would
> be "overwrite", and you need to write a mechanism to prevent this
situation.
> I must say I feel quite stupid trying to create a sulution to this for a
> MIDAS application simply to handle a standard concurrency issue. Just
> because it's "offline", and has its own strange way of dealing with these
> conflicts.
> In an OO app of mine I created a communicatiuons channel where all clients
> were informing others about what objects were editing and what objects had
> new values stored. To reduce number of conficts encountered upon "save".
But
> this was a scenario where two persons could accidentially work for half an
> hour with the same object...just to have their work discarded upon 'save'.
LOL :)
Well actually I was thinking something like that...
A message like
"You take too much time to do the opperation and another user modiffied your
data, please try again"
or...
"You take too much time to do the opperation and user Peter modiffied your
data, please try again"
Another solution...
I was also thinking that if a user modifies and object it blocks it so if
you try to modify it you will have a message like:
"User Peter is modiffiying the <Data>. Please try again latter"
But... what if peter starts a transaction goes to luch and comes an our
latte locking a critical object?
Really I havent made up my mind yet.
Wich is the best sollution for general cases?
"Bjørge Sæther" <bjorge@hahaha_itte.no> wrote in message
news:40d2...@newsgroups.borland.com...
> If your server application is the only way of access to the data, you may
of
> course cache the object for later use. But for this to have any meaning,
> objects must be either a) retrieved over and over again by many clients
> within a short while, and/or b) have a consuming construction process.
> Remember, both file system and DB server has a cache, too, so if you
> retrieve a record twice within a short while, the second one would be
> superfast. Are you sure you *need* an object cache ?
This is fun and I like to comment it. I totally agree with you. Caches are
somewhat a luxuy. When I stated to design the OPF I decided not to use cache
because they would increase the project time in 200%. But my boss (the
person who told me "Make an OPF") was somewhat impermeable to logic (I hope
he doesnt read this). He *wanted* a cache.
Despited everithing, he insisted I should add the CacheManager to the
design.
So I added the cache and had much more fun doing the project. I guess it was
a Win-Win situation.
Esteban Calabria
Hi,
> The
> question is... how would the client acces the objecs in the server? Would
> they use remote proxys instead of BO? Would they have a copy of the objects?
In our implementation there is only ever a single instance of a given
object at any one time, as it makes concurrentcy a lot easier to handle.
'Clients' tend to work with local or remove proxies, depending on where
the code is running, are the configuration of the transaction.
Hi,
> I played with EJB a couple of years ago and was VERY impressed with the
> concept, and not so impressed with the implementation (JB4)
It can be overly complex, but most recent IDE's and other techniqus make
it very simple to develop with .
> or the cost
> (none of the app servers are cheap).
There is at least three 'free' app servers, one of which would the
leading commercial one a good run for its money.
> As for 3rd party OPFs, I'm just afraid of 3rd party code.
Most Java libraries are third party, especially the J2EE stuff. SUN
provides the specs, the third parties implement them.
> Call me a scardey
> cat (sp?), but when the performance sucks eggs or the app crashes and burns
> 3 times a day, *I* am the one responsible and *I* want to be able to fix the
> problem.
The TP Java OPF's are very mature, stable and well adopted in the real
word, plus, the majority of them are open source and are well funded by
the commercial world.
I know a lot of people who do Java stuff and not one of them has even
contemplated writing their own OPF, so if you do go that route, take a
look at the options before you decide to go your own route.