Wow, MS announced a public preview of MSSQL for Linux

RHEL instructions: https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-red-hat
Ubuntu instructions: https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-ubuntu
Docker instructions: https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-docker

WTF???

I like large deployments because large deployments mean a lot of challenges 🙂 And the new challenge is: ACLs are fucking slow because EMC fails to write fast code. Let’s check how long does it take to create ACL with 60000 accessors on DFC side (I do know that it is not a good idea to create ACLs with more than 32767 accessors, but I like drawing cool charts):

public class DfAclTest extends DfcTestSupport {

    @Test
    public void aclTest() throws Exception {
        IDfSession session = getSession();
        int multiplier = 1000;
        IDfACL acl = (IDfACL) session.newObject("dm_acl");
        System.out.println("\n=========== CREATE ===========");
        long start = System.currentTimeMillis();
        for (int i = 1; i <= 60; i++) {
            grant(acl, (i - 1) * multiplier, multiplier);
            System.out.println("accessors: " + (i * multiplier) + ", time: "
                    + (System.currentTimeMillis() - start));

        }
        System.out.println("\n=========== UPDATE ===========");
        start = System.currentTimeMillis();
        for (int i = 1; i <= 60; i++) {
            grant(acl, (i - 1) * multiplier, multiplier);
            System.out.println("accessors: " + (i * multiplier) + ", time: "
                    + (System.currentTimeMillis() - start));

        }
    }

    private static void grant(IDfACL acl, int offset, int amount)
        throws DfException {
        for (int i = offset; i < amount + offset; i++) {
            acl.grant(String.valueOf(i), 3, null);
        }
    }

}

and results are really impressive:

=========== CREATE ===========
...
accessors: 60000, time: 1155926
...
=========== UPDATE ===========
...
accessors: 60000, time: 3564534

i.e. if we were need to create ACL with 60000 records it would take 20 minutes, if we decided to update each record in that ACL it would take one hour 🙂 I was unable to understand how it was possible to write so slow algorithm and decided to somehow optimize it, fortunately it didn’t take much time – I just changed two methods in com.documentum.fc.client.DfACL:

  1. findEntry – from
    for (int i = 0, limit = getAccessorCount(); i < limit; i++) {
        if (getAccessorPermitType(i) == permitType 
                && getAccessorName(i).equals(accessorName)) {

    to

            int start = findString(ACCESSOR_NAME, accessorName);
            if (start < 0) {
                return  -1;
            }
    
            for (int i = start, limit = getAccessorCount(); i < limit; i++) {
                if (getAccessorName(i).equals(accessorName)
                        && getAccessorPermitType(i) == permitType) {
    
  2. updateAuditTrail to
    return

and got another impressive results.

First optimization only:

=========== CREATE ===========
...
accessors: 60000, time: 456957
...
=========== UPDATE ===========
...
accessors: 60000, time: 537209

First and second optimizations:

=========== CREATE ===========
...
accessors: 60000, time: 136213
...
=========== UPDATE ===========
...
accessors: 60000, time: 131164

Chart:

aspects or not aspects…

Some time ago I wrote a post about no progress in Documentum platform, and the opinion about aspects feature was, I would say, neutral rather than negative, now, after two years of using aspects I can say without doubts: never ever use Documentum aspects, avoid repeating my mistake – EMC’s implementations is completely unreliable and slow. Let’s explain…

At first, I do not want to discuss DFC Aspect Model:

because it hard to imagine situation when you really need to build TBO classes in runtime – EMC tried use this feature in XCP2 and had failed. So, this feature is defective by design, and I would like to talk about additional attributes which we can introduce using aspects.

Well, how does Content Server behave when it fetches aspect-enabled objects? Let’s discover it’s behaviour using previously demonstrated technique (I specially issue two fetches because the first one performs a lot of irrelevant caching queries related to information about types and TBOs):

Connected to Documentum Server running Release 7.2.0030.0195  Linux64.Oracle
Session id is s0
API> apply,c,,SQL_TRACE,SESSION_ID,S,01024be980005db7,LEVEL,I,10
...
q0
API> ?,c,q0
result      
------------
T           
(1 row affected)

API> fetch,c,09024be98001f94e
...
OK
API> fetch,c,09024be98001f94f
...
OK
API> Bye

and the result is:

-- determine object type by r_object_id
SELECT dm_dbalias_B.I_TYPE
  FROM DMI_OBJECT_TYPE dm_dbalias_B
 WHERE dm_dbalias_B.R_OBJECT_ID = :dmb_handle;

-- retrieve object's data from database
  SELECT *
    FROM CUSTOM_TYPE_RV dm_dbalias_B, CUSTOM_TYPE_SV dm_dbalias_C
   WHERE (    dm_dbalias_C.R_OBJECT_ID = :dmb_handle
          AND dm_dbalias_C.R_OBJECT_ID = dm_dbalias_B.R_OBJECT_ID)
ORDER BY dm_dbalias_B.R_OBJECT_ID, dm_dbalias_B.I_POSITION;

-- retrieve aspect's data from database
  SELECT *
    FROM DMI_03024BE98000025B_RV dm_dbalias_B,
         DMI_03024BE98000025B_SV dm_dbalias_C
   WHERE (    dm_dbalias_C.R_OBJECT_ID = :dmb_handle
          AND dm_dbalias_C.R_OBJECT_ID = dm_dbalias_B.R_OBJECT_ID)
ORDER BY dm_dbalias_B.R_OBJECT_ID, dm_dbalias_B.I_POSITION;

-- check whether object is actual or not
SELECT dm_dbalias_B.R_OBJECT_ID
  FROM DM_SYSOBJECT_S dm_dbalias_B
 WHERE (    dm_dbalias_B.R_OBJECT_ID = :dmb_objectp
        AND dm_dbalias_B.I_VSTAMP = :dmb_versionp);

If you compare these queries with queries described in What happens when smart guy does not sit back blogpost you will find that the existence of aspect introduces two new queries, i.e.:

-- retrieve aspect's data from database
  SELECT *
    FROM DMI_03024BE98000025B_RV dm_dbalias_B,
         DMI_03024BE98000025B_SV dm_dbalias_C
   WHERE (    dm_dbalias_C.R_OBJECT_ID = :dmb_handle
          AND dm_dbalias_C.R_OBJECT_ID = dm_dbalias_B.R_OBJECT_ID)
ORDER BY dm_dbalias_B.R_OBJECT_ID, dm_dbalias_B.I_POSITION;

-- check whether object is actual or not
SELECT dm_dbalias_B.R_OBJECT_ID
  FROM DM_SYSOBJECT_S dm_dbalias_B
 WHERE (    dm_dbalias_B.R_OBJECT_ID = :dmb_objectp
        AND dm_dbalias_B.I_VSTAMP = :dmb_versionp);

So, it is obvious that aspects have negative performance impact, what about reliability? Try to guess what is the purpose of the last select statement:

-- check whether object is actual or not
SELECT dm_dbalias_B.R_OBJECT_ID
  FROM DM_SYSOBJECT_S dm_dbalias_B
 WHERE (    dm_dbalias_B.R_OBJECT_ID = :dmb_objectp
        AND dm_dbalias_B.I_VSTAMP = :dmb_versionp);

Though, there is much simpler question – why, when fetching object’s data from database, Content Server issues following statement:

  SELECT *
    FROM CUSTOM_TYPE_RV dm_dbalias_B, CUSTOM_TYPE_SV dm_dbalias_C
   WHERE (    dm_dbalias_C.R_OBJECT_ID = :dmb_handle
          AND dm_dbalias_C.R_OBJECT_ID = dm_dbalias_B.R_OBJECT_ID)
ORDER BY dm_dbalias_B.R_OBJECT_ID, dm_dbalias_B.I_POSITION;

but not two much more simple statements:

  SELECT *
    FROM CUSTOM_TYPE_SV dm_dbalias_C
   WHERE (    dm_dbalias_C.R_OBJECT_ID = :dmb_handle);

  SELECT *
    FROM CUSTOM_TYPE_RV dm_dbalias_B
   WHERE (    dm_dbalias_B.R_OBJECT_ID = :dmb_handle)
ORDER BY dm_dbalias_B.R_OBJECT_ID, dm_dbalias_B.I_POSITION;

?

The clue for the last question is a statement-level read consistency, i.e. in case of single query the result is always consistent, in case of two queries – doesn’t. Now the purpose of the last select statement in aspect example is clear: Content Server successively retrieves object’s data, aspect’s data, and after that Content Server checks whether the object have been changed since the first query or not. And now the most interesting thing: what happens if last select statement returns nothing? Nothing good. It is really hard to reproduce the situation but I have succeeded and wrote following test case:

/**
 * @author Andrey B. Panfilov <andrey@panfilov.tel>
 */
public class DfExceptionsTest extends DfcTestSupport {

    @Test
    @Ignore
    public void testSoftFetch() throws Exception {
        final IDfSession s1 = Sessions.brandNew(getSession()
                .getSessionManager(), getSession().getDocbaseName());
        final IDfSession s2 = Sessions.brandNew(getSession()
                .getSessionManager(), getSession().getDocbaseName());

        TestFetch testFetch = new TestFetch(s2, "09024be98001f94e");
        Thread t = new Thread(testFetch);
        t.start();

        ApplyExecSQL cmd = (ApplyExecSQL) DfAdminCommand
                .getCommand(IDfAdminCommand.APPLY_EXEC_SQL);
        cmd.setQuery("update dm_sysobject_s set i_vstamp=i_vstamp+1 "
                + "where r_object_id='09024be98001f94e'");

        for (int i = 0; i < 10000; i++) {
            cmd.execute(s1);
        }

        t.interrupt();
        assertFalse(testFetch._failed);
    }

    class TestFetch implements Runnable {

        private final IDfSession _session;

        private final String _objectId;

        private boolean _failed;

        public TestFetch(IDfSession session, String objectId) {
            _session = session;
            _objectId = objectId;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 100000; i++) {
                    _session.getObject(DfIdUtil.getId(_objectId));
                    _session.flushObject(DfIdUtil.getId(_objectId));
                }
            } catch (DfException ex) {
                ex.printStackTrace();
                _failed = true;
            }
        }
    }

}

which reproduces two different exceptions:

DfException:: THREAD: Thread-9; MSG: [DM_OBJ_MGR_E_UNABLE_TO_FETCH_CONSISTENT_OBJECT_SNAPSHOT]
   error:  "Unable to fetch object 09024be98001f94e alongwith its aspect attributes with consistency"; ERRORCODE: 100; 
NEXT: null
	at com.documentum.fc.client.impl.docbase.DocbaseExceptionMapper.newException(DocbaseExceptionMapper.java:57)
	at com.documentum.fc.client.impl.connection.docbase.MessageEntry.getException(MessageEntry.java:39)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseMessageManager.getException(DocbaseMessageManager.java:137)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.checkForMessages(NetwiseDocbaseRpcClient.java:310)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.applyForObject(NetwiseDocbaseRpcClient.java:653)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection$8.evaluate(DocbaseConnection.java:1380)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.evaluateRpc(DocbaseConnection.java:1139)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.applyForObject(DocbaseConnection.java:1372)
	at com.documentum.fc.client.impl.docbase.DocbaseApi.parameterizedFetch(DocbaseApi.java:107)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.fetchFromServer(PersistentDataManager.java:191)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.getData(PersistentDataManager.java:82)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObjectFromServer(PersistentObjectManager.java:355)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObject(PersistentObjectManager.java:311)
	at com.documentum.fc.client.impl.session.Session.getObject(Session.java:963)
	at com.documentum.fc.client.impl.session.SessionHandle.getObject(SessionHandle.java:653)

and

DfIdNotFoundException:: THREAD: Thread-9; MSG: [DM_API_E_EXIST]
  error:  "Document/object specified by 09024be98001f94e does not exist."; ERRORCODE: 100; 
NEXT: DfException:: THREAD: Thread-9; MSG: [DM_OBJ_MGR_E_UNABLE_TO_FETCH_CONSISTENT_OBJECT_SNAPSHOT]
  error:  "Unable to fetch object 09024be98001f94e alongwith its aspect attributes with consistency"; ERRORCODE: 100; 
NEXT: null
	at com.documentum.fc.client.impl.docbase.DocbaseExceptionMapper.newException(DocbaseExceptionMapper.java:49)
	at com.documentum.fc.client.impl.connection.docbase.MessageEntry.getException(MessageEntry.java:39)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseMessageManager.getException(DocbaseMessageManager.java:137)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.checkForMessages(NetwiseDocbaseRpcClient.java:310)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.applyForObject(NetwiseDocbaseRpcClient.java:653)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection$8.evaluate(DocbaseConnection.java:1380)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.evaluateRpc(DocbaseConnection.java:1139)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.applyForObject(DocbaseConnection.java:1372)
	at com.documentum.fc.client.impl.docbase.DocbaseApi.parameterizedFetch(DocbaseApi.java:107)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.fetchFromServer(PersistentDataManager.java:191)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.getData(PersistentDataManager.java:82)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObjectFromServer(PersistentObjectManager.java:355)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObject(PersistentObjectManager.java:311)
	at com.documentum.fc.client.impl.session.Session.getObject(Session.java:963)
	at com.documentum.fc.client.impl.session.SessionHandle.getObject(SessionHandle.java:653)
Caused by: DfException:: THREAD: Thread-9; MSG: [DM_SYSOBJECT_E_CANT_FETCH_INVALID_ID]
  error:  "Cannot fetch a sysobject - Invalid object ID : 09024be98001f94e"; ERRORCODE: 100; 
NEXT: null
	... 19 more
Followed by: DfException:: THREAD: Thread-9; MSG: [DM_OBJ_MGR_E_UNABLE_TO_FETCH_CONSISTENT_OBJECT_SNAPSHOT]
  error:  "Unable to fetch object 09024be98001f94e alongwith its aspect attributes with consistency"; ERRORCODE: 100; 
NEXT: null
	at com.documentum.fc.client.impl.docbase.DocbaseExceptionMapper.newException(DocbaseExceptionMapper.java:57)
	at com.documentum.fc.client.impl.connection.docbase.MessageEntry.getException(MessageEntry.java:39)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseMessageManager.getException(DocbaseMessageManager.java:137)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.checkForMessages(NetwiseDocbaseRpcClient.java:310)
	at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.applyForObject(NetwiseDocbaseRpcClient.java:653)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection$8.evaluate(DocbaseConnection.java:1380)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.evaluateRpc(DocbaseConnection.java:1139)
	at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.applyForObject(DocbaseConnection.java:1372)
	at com.documentum.fc.client.impl.docbase.DocbaseApi.parameterizedFetch(DocbaseApi.java:107)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.fetchFromServer(PersistentDataManager.java:191)
	at com.documentum.fc.client.impl.objectmanager.PersistentDataManager.getData(PersistentDataManager.java:82)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObjectFromServer(PersistentObjectManager.java:355)
	at com.documentum.fc.client.impl.objectmanager.PersistentObjectManager.getObject(PersistentObjectManager.java:311)
	at com.documentum.fc.client.impl.session.Session.getObject(Session.java:963)
	at com.documentum.fc.client.impl.session.SessionHandle.getObject(SessionHandle.java:653)

The last one is extremely weird because DFC hides the real error under DfIdNotFoundException. Now let’s check what Content Server thinks about DM_OBJ_MGR_E_UNABLE_TO_FETCH_CONSISTENT_OBJECT_SNAPSHOT error:

SEVERITY: ERROR
DESCRIPTION: D Unable to fetch object %s alongwith its aspect attributes with consistency
CAUSE: This error occurs when a client fetches an object that has aspect attributes, and that either the normal or aspect attributes have changed perhaps by a different session while Content Server was fetching the aspect attributes after fetching the normal attributes from the database.
ACTION: Application should refetch the object.
PARAMS:

Why does the fucking DFC do not reiterate fetch after DM_OBJ_MGR_E_UNABLE_TO_FETCH_CONSISTENT_OBJECT_SNAPSHOT error?

Speak numbers in Java

Today I got some “feedback” about previous post, and “feedback” was: I can’t read Russian. So, now I will try to write the same in English 🙂

The problem is: sometimes in formal documents we mustn’t use numbers – only words are accepted, i.e. instead of writing something like “this document is valid until November 16, 2016” I must write something like “this document is valid until the sixteenth of November, twenty sixteen” or “this document is valid until November the sixteenth, twenty sixteen”, unfortunately example above is unable to express the complexity of the problem for Russian, because in Russian besides ordinal/cardinal numbers and grammatical gender we also have grammatical cases, and the most close writing for “November 16, 2016” in Russian would be: “sixteenth of November of two thousand sixteenth year” – actually instead of “of November” and “of year” I need to use genitive case of corresponding words, moreover for “sixteenth” I also need to use genitive case. So, what to do? Actually for cardinal numbers the algorithm seems to be trivial: in case of Russia we need to define writing for following “illogical” numbers: 1-20 with step of 1, 30-90 with step of 10, 100-900 with step of 100, 1000, 1000^2, 1000^3, etc, and after that write an algorithm which will divide numbers and concatenate strings. However ordinal numbers and genitive case complicate everything (for example, writing for ordinal 2000 is not “two thousandth” but something like “twothousandth”), so I decided to check whether someone already did the same and have found that the guys from ICU project already implemented everything I was looking for:

ULocale locale = new ULocale("en");
SimpleDateFormat dateFmt = new SimpleDateFormat("'the' d 'of' MMMM, yyyy", locale);
RuleBasedNumberFormat dayFmt = new RuleBasedNumberFormat(locale, RuleBasedNumberFormat.SPELLOUT);
dayFmt.setDefaultRuleSet("%spellout-ordinal");
dateFmt.setNumberFormat("d", dayFmt);
RuleBasedNumberFormat yearFmt = new RuleBasedNumberFormat(locale, RuleBasedNumberFormat.SPELLOUT);
yearFmt.setDefaultRuleSet("%spellout-numbering-year");
dateFmt.setNumberFormat("y", yearFmt);
System.out.println(dateFmt.format(new Date()));

produces:

the sixteenth of November, twenty sixteen

Russian example:

ULocale locale = new ULocale("ru");
SimpleDateFormat dateFmt = new SimpleDateFormat("d MMMM yyyy 'года'", locale);
RuleBasedNumberFormat dayFmt = new RuleBasedNumberFormat(locale, RuleBasedNumberFormat.SPELLOUT);
dayFmt.setDefaultRuleSet("%spellout-ordinal-neuter-genitive");
dateFmt.setNumberFormat("d", dayFmt);
RuleBasedNumberFormat yearFmt = new RuleBasedNumberFormat(locale, RuleBasedNumberFormat.SPELLOUT);
yearFmt.setDefaultRuleSet("%spellout-numbering-year");
dateFmt.setNumberFormat("y", yearFmt);
System.out.println(dateFmt.format(new Date()));

produces:

шестнадцатого ноября две тысячи шестнадцатого года

Число прописью в java

Сегодня столкнулся с тем, что потребовалось превращать числа и даты в слова, например “срок действия – 15 ноября 2016” должно превратиться в “настоящая доверенность выдана сроком до пятнадцатого ноября две тысячи пятнадцатого года”, т.е. имеем дело с порядковыми числительными мужскогосреднего рода в родительном падеже. Для количественных числительных в именительном падеже задача видится тривиальной: нужно определить как писать степени тысяч (т.е. тысяча/тысячи/тысяч, миллион/миллиона/миллионов, миллиард/миллиарда/миллиардов) и числительные до тысячи (т.е. один-двадцать, тридцать-девяносто, сто-девятьсот), ну а потом делить числа и складывать строки. Для порядковых же числительных писанины прибавляется, потому что нужно или определять еще как писать сотый, тысячный, двухтысячный, миллионный и т.д или писать менее тривиальный код, поэтому закралось подозрение что кто-то такое уже реализовывал и не стоит изобретать велосипед, и да, действительно, в упоминавшейся ранее библиотеке ICU такая возможность есть:

RuleBasedNumberFormat format = new RuleBasedNumberFormat(new ULocale("ru"), RuleBasedNumberFormat.SPELLOUT);
for (String ruleSet : new String[] { "%spellout-cardinal-feminine-ablative",
        "%spellout-cardinal-feminine-accusative", "%spellout-cardinal-feminine-dative",
        "%spellout-cardinal-feminine-genitive", "%spellout-cardinal-feminine-locative",
        "%spellout-cardinal-feminine", "%spellout-cardinal-masculine-ablative",
        "%spellout-cardinal-masculine-accusative", "%spellout-cardinal-masculine-dative",
        "%spellout-cardinal-masculine-genitive", "%spellout-cardinal-masculine-locative",
        "%spellout-cardinal-masculine", "%spellout-cardinal-neuter-ablative",
        "%spellout-cardinal-neuter-accusative", "%spellout-cardinal-neuter-dative",
        "%spellout-cardinal-neuter-genitive", "%spellout-cardinal-neuter-locative", 
        "%spellout-cardinal-neuter", "%spellout-cardinal-plural-ablative", 
        "%spellout-cardinal-plural-accusative", "%spellout-cardinal-plural-dative", 
        "%spellout-cardinal-plural-genitive", "%spellout-cardinal-plural-locative", 
        "%spellout-cardinal-plural", "%spellout-numbering-year",
        "%spellout-numbering", "%spellout-ordinal-feminine-ablative", 
        "%spellout-ordinal-feminine-accusative", "%spellout-ordinal-feminine-dative",
        "%spellout-ordinal-feminine-genitive", "%spellout-ordinal-feminine-locative", 
        "%spellout-ordinal-feminine", "%spellout-ordinal-masculine-ablative", 
        "%spellout-ordinal-masculine-accusative", "%spellout-ordinal-masculine-dative", 
        "%spellout-ordinal-masculine-genitive", "%spellout-ordinal-masculine-locative", 
        "%spellout-ordinal-masculine", "%spellout-ordinal-neuter-ablative", 
        "%spellout-ordinal-neuter-accusative", "%spellout-ordinal-neuter-dative", 
        "%spellout-ordinal-neuter-genitive", "%spellout-ordinal-neuter-locative", 
        "%spellout-ordinal-neuter", "%spellout-ordinal-plural-ablative",
        "%spellout-ordinal-plural-accusative", "%spellout-ordinal-plural-dative",
        "%spellout-ordinal-plural-genitive", "%spellout-ordinal-plural-locative",
        "%spellout-ordinal-plural", }) {
    System.out.println(format.format(9999, ruleSet));
}
девятью тысячами девятьюстами девяноста девятью
девять тысяч девятьсот девяносто девять
девяти тысячам девятистам девяноста девяти
девяти тысяч девятисот девяноста девяти
девяти тысячах девятистах девяноста девяти
девять тысяч девятьсот девяносто девять
девятью тысячами девятьюстами девяноста девятью
девять тысяч девятьсот девяносто девять
девяти тысячам девятистам девяноста девяти
девяти тысяч девятисот девяноста девяти
девяти тысячах девятистах девяноста девяти
девять тысяч девятьсот девяносто девять
девятью тысячами девятьюстами девяноста девятью
девять тысяч девятьсот девяносто девять
девяти тысячам девятистам девяноста девяти
девяти тысяч девятисот девяноста девяти
девяти тысячах девятистах девяноста девяти
девять тысяч девятьсот девяносто девять
девятью тысячами девятьюстами девяноста девятью
девять тысяч девятьсот девяносто девять
девяти тысячам девятистам девяноста девяти
девяти тысяч девятисот девяноста девяти
девяти тысячах девятистах девяноста девяти
девять тысяч девятьсот девяносто девять
девять тысяч девятьсот девяносто девятого
девять тысяч девятьсот девяносто девять
девять тысяч девятьсот девяносто девятой
девять тысяч девятьсот девяносто девятую
девять тысяч девятьсот девяносто девятой
девять тысяч девятьсот девяносто девятой
девять тысяч девятьсот девяносто девятой
девять тысяч девятьсот девяносто девятая
девять тысяч девятьсот девяносто девятым
девять тысяч девятьсот девяносто девятый
девять тысяч девятьсот девяносто девятому
девять тысяч девятьсот девяносто девятого
девять тысяч девятьсот девяносто девятом
девять тысяч девятьсот девяносто девятый
девять тысяч девятьсот девяносто девятым
девять тысяч девятьсот девяносто девятое
девять тысяч девятьсот девяносто девятому
девять тысяч девятьсот девяносто девятого
девять тысяч девятьсот девяносто девятом
девять тысяч девятьсот девяносто девятое
девять тысяч девятьсот девяносто девятыми
девять тысяч девятьсот девяносто девятые
девять тысяч девятьсот девяносто девятым
девять тысяч девятьсот девяносто девятых
девять тысяч девятьсот девяносто девятых
девять тысяч девятьсот девяносто девятые

DBK and LTK structure

Not sure about terminology, but it seems that what is stored in dm_docbase_config.i_crypto_key is a DBK, and what is stored in dm_docbase_config.i_ticket_crypto_key is a LTK:

[dmadmin@docu72dev01 bin]$ strings -a documentum | grep _KEY_CLASS
DM_DBK_KEY_CLASS
DM_LTK_KEY_CLASS
DM_FSK_KEY_CLASS
DM_PPK_KEY_CLASS
...

3DES:

AES:

The next challenges are:

  • Install TCS option and understand storage key structure
  • Create offline tool generating login tickets

AEK and DBK parsers are already done: https://github.com/andreybpanfilov/prodctm/tree/master/prodctm-util/src/main/java/pro/documentum/util/crypto