Data Repoint NULLS Data In New Class Table When Old Class Property Is Changed & Saved
We are attempting to "Repoint" old class data to new class data to save disk space and data redundancy across multiple tables. This works to a point. In essence the two classes are sharing the same data / Index / stream globals. But if an ID in the Old_Class is opened, a property is modified, and saved the property that is in the New_Class (but not in the Old_Class) is NULLed / blanked.
Simplified explanation of data and what’s occurring.
| Old Class | Values | New Class | Values | |
| First_Name | John | First_Name | John | |
| Middle_Initial | Q | Middle_Initial | Q | |
| Last_Name | Public | Last_Name | Public | |
| Date_Of_Birth | 1/1/1965 | Date_Of_Birth | 1/1/1965 | |
| SSN | 123-45-6789 | SSN | 123-45-6789 | |
| Marital_Status | Married |
When the Old_Class is opened and any field in the Old_Class is modified and a %Save is run the Marital_Status becomes NULL / Blank.
Why does a %Save on the Old_Class result in a NULL / Blank value in the data storage global and in the New_Class table? We have already checked Journaling and it sets a null into the $List position for "Marital_Status".
Is there a means to open the record via the Old_Class alter a property value, and perform a %Save without “losing” the value in the Marital_Status field in the New_Class?
Comments
- With %OpenId you create a complete internal local copy of your object
- With %Save it is written back as complete as is was. NOT just changed properties as you might expect And non- defined properties vanish
- Any change that happened in between from some other process is lost
- You may need some locking logic to prevent simultaneous access by 2 instances eg. locking the Id for exclusive access by 1 single process.
- In your case it may even happen in 1 process with Old_Class vs, New_Class
Re-reading your question, I understand that properties in
New_class are not identical and synchronized with Old_class ?
So even Storage Definition might be different ?
IF YES, I'd say it's highly risky!
I'm not sure, do I understand you correctly...
If you had a class and copied that class to a new class with some more properties, something like this
Class DC.OldData Extends %Persistent
{
Property FirstName As %String;
Property LastName As %String;
/// Redirect the %Open(), %OpenId() to the new class
///
ClassMethod %OnDetermineClass(oid As %ObjectIdentity, ByRef class As %String) As %Status [ ServerOnly = 1 ]
{
Set class="DC.NewData"
Quit $$$OK
}
/// Prevent creating a new instance of the old class
Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
Quit '$$$OK
}
}
Class DC.NewData Extends %Persistent
{
Property FirstName As %String;
Property LastName As %String;
Property MartialStatus As %String;
}ATTENTION ! This made me nervous.
In essence, the two classes are sharing the same data / Index / stream globals.
This means there is also
Otherwise, it wouldn't compile
Robert,
We learned that when we were doing proof of concept on the idea of pointing an older class to the new classes data storage.
And I don't think locking is an issue, at least in my testing, because I was doing my testing on a local laptop install of IRIS so there was no contention with another process opening or changing the data.
OK,
but prerequisit is, that both definitions are identical.
Especially in the Storage definition, where properties are mapped to globals
Robert,
They are identical EXCEPT the new class may have one...two...three...etc new / additional properties / fields.
So it's clear.
Old_Class loads old defined properties in a local Object structure
and writes only this structure back.
It has no idea of New properties and doesn't write it.
Just a guess:
$LB(...) in ^GlobalD of Old_class is shorter than for New_class
So %Save() of Old_class is a data killer.
I'm struggling to understand your original question "We are attempting to "Repoint" old class data to new class data to save disk space and data redundancy across multiple tables. "
Will "Repoint"ing acutall save disk space? At first glance it would be hard to see how that would make a significant difference. It might be true that the old class has a number of properties that are no longer used so in the physical storage you have a number of unused storage element but I wouldnt think that would make a significant difference.
One area that can make a difference is if your classes utilize bitmap indices and if you have rapidly changing data with fields are indexed. IRIS delivers %SYS.Maint.Bitmap which is described as
This utility is used to compact bitmap/bitslice indices. Over time in a volatile table (think lots of INSERTs and DELETEs) the storage for a bitmap index may become less efficient. To a lesser extent index value changes, i.e. UPDATES, can also degrade bitmap performance.
This utility can by run on a live system. It will loop over all bitmap/bitslice indices in a class or namespace and compact them by either removing a chunk that is all zeros or by using $BITLOGIC() to compact the bits.
Consider Ens.MessageHeader which has a bitmap index on Status where the Status values are
1=Crerated
2=Queued
3=Delivered
4=Discarded
5=Suspended
6=Deferred
7=Aborted
8=Error
9=Completed
You can imagine that a Message Header is save with
Event 1 Status=Created
Event 2 Status=Queued
Event 3 Status=Delivered
Event 4 Status = Completed
in the normal course of processing
This would have the side effect of
1. Event 1 setting the Status =Created bit on
2. Event 2 Setting the Status=Created bit Off, Setting the Status=Queued On
3. Event 3 Setting the Status=Queued bit Off, Setting the Status=Delivered On
4. Event 4 Setting the Status=Delivered bit Off, Setting the Status=Completed On
all of this means that the bitmap index for
Status=Created, Queued, Delivered over time is just a series of off bits and in many cases the entire bitchunk is just a bunch of off bits(although there is some compression here).
I have utilized %SYS.Maint.Bitmap on systems that have existed for many years and saw some noticable space savings. However, once run it doesnt seem to make a lot of sense to run very often.
This can possibly work if you make %Persistent a secondary superclass. In that case, new properties would go to a separate subscript, and your setup can maybe work (requires testing).
Storage works like this (parent has N properties, Child - X):
Child Extends %Persistent, Parent:
^a(id) = $lb("", prop1, prop2, ..., propX)Child Extends Parent, %Persistent:
^a(id) = $lb("Child", propA, propB, ..., propN)
^a(id, "Child") = $lb(propO, propP, ..., propX)