!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: rcindexdictionary.gs,v 1.16 2008-01-09 22:50:14 stever Exp $
!
! Superclass Hierarchy:
!   RcIndexDictionary, RcKeyValueDictionary, AbstractDictionary,
!   Collection, Object.
!
!=========================================================================

! class created in idxclasses.topaz

removeallmethods RcIndexDictionary
removeallclassmethods RcIndexDictionary

category: 'For Documentation Installation only'
classmethod: RcIndexDictionary
installDocumentation

| doc txt |
doc := GsClassDocumentation _newForPrivateGsClass: self.

txt := (GsDocText new) details:
''.

doc documentClassWith: txt.

self description: doc.
%

! ------------------- Class methods for RcIndexDictionary
category: 'Instance Creation'
classmethod: RcIndexDictionary
new

"Returns a RcIndexDictionary with the default table size."

^ self new: self defaultBasicSize
%

category: 'Constants'
classmethod: RcIndexDictionary
defaultBasicSize

"Returns the default basic size."

^ 2003
%

! ------------------- Instance methods for RcIndexDictionary
category: 'Updating'
method: RcIndexDictionary
_at: aKey put: aValue term: pathTerm logging: aBoolean

"Stores the aKey/aValue/pathTerm triplet in the index dictionary.  Rebuilds the
 hash table if the addition caused the number of collisions to exceed the limit
 allowed.  The argument aBoolean determines if an entry is placed in the redo
 log for this operation."

| collBkt |

okToUpdate
  ifFalse: [ | hash |
    hash := (aKey identityHash \\ self _basicSize) + 1.
    " workingSetInterval is the number of collision buckets covered "
    hash < workingSetInterval
      ifFalse: [ | offset |
        " get Array of IndexDictionaryEntryHolders "
        offset := (hash \\ entryHolders size) + 1.
        (entryHolders at: offset)
          addKey: aKey
          value: aValue
          term: pathTerm
          from: entryHolders
          at: offset.
        ^ true
      ]
  ].

collBkt := self indexBucketFor: aKey.
collBkt at: aKey put: aValue term: pathTerm root: self logging: aBoolean.

aBoolean
  ifTrue: [
    self logAt: aKey put: aValue term: pathTerm inCollisionBucket: collBkt
  ].

^ true
%

category: 'Removing'
method: RcIndexDictionary
_removeKey: aKey value: aValue term: pathTerm

"Removes the key/value/pathTerm triplet from the receiver.  Returns true if the
 removal was successful, false if not."

| collisionBkt |
collisionBkt := self indexBucketFor: aKey.

collisionBkt
    removeKey: aKey
    value: aValue
    term: pathTerm
    root: self
    logging: false
    ifAbsent: [ ^ false ].

^ true
%

category: 'Updating'
method: RcIndexDictionary
at: aKey put: aValue term: pathTerm

"Stores the aKey/aValue/pathTerm triplet in the index dictionary.  Returns the
 value."

self _at: aKey put: aValue term: pathTerm logging: true.
^ aValue
%

category: 'Accessing'
method: RcIndexDictionary
at: aKey term: pathTerm ifAbsent: aBlock

"Returns the value that corresponds to aKey/pathTerm.  If no such
 key/value/pathTerm triplet exists, returns the result of evaluating the
 zero-argument block aBlock."

^ (self indexBucketFor: aKey) at: aKey term: pathTerm ifAbsent: aBlock
%

category: 'Constants'
method: RcIndexDictionary
collisionLimitPerBucket

"Returns the number of entries in a collision bucket such that if a greater
 number is inserted, the hash dictionary will grow.  This number is chosen such
 that a collision bucket will occupy a single page
 (collisionLimitPerBucket * entrySize <= 2000)."

^ 660
%

category: 'Initializing'
method: RcIndexDictionary
initializeWorkingSetInterval: indexObj

"Determines the working set interval (the number of collision buckets that are
 considered for writing during index creation).  If the instance variable
 'okToUpdate' is set to true, then index creation does not control the working
 set (and the working set interval is not used)."


" indicate whether it is not ok to update the dictionary
  (so look at working set instead) "
" only use this algorithm if NSC size is greater than 1MByte "
okToUpdate := indexObj nscRoot size < 1000000.

workingSetInterval := self getWorkingSetInterval: indexObj nscRoot.

okToUpdate
  ifFalse: [ | array |
    array := Array new: (self _basicSize // workingSetInterval).
    1 to: array size do: [ :i |
      array at: i put: (IndexDictionaryEntryHolder new indexDictionary: self)
    ].
    entryHolders := array.
  ]
%

category: 'Initializing'
method: RcIndexDictionary
initialize: itsSize

"Initializes the receiver and sets its collisionLimitPerBucket."


super initialize: itsSize.
collisionLimitPerBucket := self collisionLimitPerBucket.
%

category: 'Private'
method: RcIndexDictionary
logAt: aKey put: aValue term: pathTerm inCollisionBucket: aCollisionBkt

"Create a log entry for adding the given key, value, and pathTerm.  The
 collision bucket is the object on which a conflict may occur."

| logEntry |
logEntry := LogEntry new.
logEntry receiver: self;
    selector: #_at:put:term:logging:;
    argArray: #[ aKey, aValue, pathTerm, false ].
System redoLog addLogEntry: logEntry forConflictObject: aCollisionBkt
%

category: 'Private'
method: RcIndexDictionary
logRemoveKey: aKey value: aValue term: pathTerm
  inCollisionBucket: aCollisionBkt

"Creates a log entry for removing the given key/value/pathTerm triplet.  The
 collision bucket is the object on which a conflict may occur."

| logEntry |
logEntry := LogEntry new.
logEntry receiver: self;
    selector: #_removeKey:value:term:;
    argArray: #[ aKey, aValue, pathTerm ].
System redoLog addLogEntry: logEntry forConflictObject: aCollisionBkt
%

category: 'Hashing'
method: RcIndexDictionary
rebuildTable: expectedGrowth for: indexObj

"Increases the size of the root object and rehashes the contents of the
 collision buckets."

| newSize array collBkt constraintClass origSize begin indexManager |
origSize := self _basicSize.

"Don't rebuild table if the size isn't any bigger"
(self shouldRebuildTable: expectedGrowth) not
  ifTrue: [ ^self ].
newSize := (Integer _selectedPrimeGreaterThan: origSize * 2 - 1).

constraintClass := self class varyingConstraint.
array := Array new: origSize.
" store all buckets in an Array "
indexManager := self indexManager.
1 to: origSize do: [ :i |
  array at: i put: (self _at: i).
  self _at: i put: constraintClass new.
  indexManager commitIndexMaintenance: indexObj at: i.
].

super _basicSize: newSize.
origSize + 1 to: newSize do: [ :i |
  self _at: i put: constraintClass new.
  indexManager commitIndexMaintenance: indexObj at: i.
].

begin := (array _at: 1) _numCacheSlots + 1.
1 to: origSize do: [ :i |
  collBkt := array _at: i.
  begin to: begin + (collBkt numElements * 3) - 1 by: 3 do:
    [ :j |
    | jval |
    jval := (collBkt _at: j+2).
    (BucketValueBag _hasInstance: jval)
      ifTrue: [
        jval do:
          [:realValue |
          self _at: (collBkt _at: j)
            put: realValue
            term: (collBkt _at: j+1)
            logging: false.
          indexManager commitIndexMaintenance: indexObj at: i.
          ].
        ]
      ifFalse: [
        self _at: (collBkt _at: j)
          put: (collBkt _at: j+2)
          term: (collBkt _at: j+1)
          logging: false.
        indexManager commitIndexMaintenance: indexObj at: i.
        ]
     ].

  collBkt reset.
].

%

category: 'ObsoleteIDX - Hashing'
method: RcIndexDictionary
rebuildTable

"OBSOLETE
 
 This method should no longer be used. See rebuildTable:for: for correct
 usage.

 Increases the size of the root object and rehashes the contents of the
 collision buckets."

| newSize array collBkt constraintClass origSize begin |
" Let's not be so aggressive on table growth "
origSize := self _basicSize.
newSize := origSize * 2 - 1.

constraintClass := self class varyingConstraint.
array := Array new: origSize.
" store all buckets in an Array "
1 to: origSize do: [ :i |
  array at: i put: (self _at: i).
  self _at: i put: constraintClass new.
].

super _basicSize: newSize.
origSize + 1 to: newSize do: [ :i |
  self _at: i put: constraintClass new.
].

begin := (array _at: 1) _numCacheSlots + 1.

1 to: origSize do: [ :i |
  collBkt := array _at: i.
  begin to: begin + (collBkt numElements * 3) - 1 by: 3 do:
    [ :j |
    | jval |
    jval := (collBkt _at: j+2).
    (BucketValueBag _hasInstance: jval)
      ifTrue: [
        jval do:
          [:realValue |
          self _at: (collBkt _at: j)
            put: realValue
            term: (collBkt _at: j+1)
            logging: false
          ]
        ]
      ifFalse: [
        self _at: (collBkt _at: j)
          put: (collBkt _at: j+2)
          term: (collBkt _at: j+1)
          logging: false
        ]
     ].

  collBkt reset.
]
%

category: 'Removing'
method: RcIndexDictionary
removeKey: anObject ifAbsent: aBlock

"Disallowed.  Use removeKey:value:ifAbsent: instead."

self shouldNotImplement: #removeKey:ifAbsent:
%

category: 'Removing'
method: RcIndexDictionary
removeKey: aKey
value: aValue
term: pathTerm
logging: aBoolean
ifAbsent: aBlock

"Remove the key/value/pathTerm triplet from the receiver.  If no
 key/value/pathTerm triplet is present, evaluate the zero-argument block
 aBlock.  Returns true if there are any other entries with the same key/term
 in the dictionary, and returns false otherwise."

| collisionBkt more doLogging |
collisionBkt := self indexBucketFor: aKey.

doLogging := aBoolean.
more := collisionBkt
    removeKey: aKey
    value: aValue
    term: pathTerm
    root: self
    logging: doLogging
    ifAbsent: [
        " if entry not found, do not log the removal "
        doLogging := false.
        aBlock value.
    ].

doLogging
    ifTrue: [ " log the removal in the redo log "
        self logRemoveKey: aKey
            value: aValue
            term: pathTerm
            inCollisionBucket: collisionBkt
    ].

^ more
%

category: 'Removing'
method: RcIndexDictionary
removeKey: aKey value: aValue term: pathTerm logging: aBoolean

"Remove the key/value/pathTerm triplet from the receiver.  Returns true
 if there are any other entries with the same key/term in the dictionary,
 and returns false otherwise."

| collisionBkt more doLogging |
collisionBkt := self indexBucketFor: aKey.
doLogging := aBoolean.

more := collisionBkt
    removeKey: aKey
    value: aValue
    term: pathTerm
    root: self
    logging: doLogging
    ifAbsent: [
        " if entry not found, do not log the removal "
        doLogging := false.
    ].

doLogging
    ifTrue: [ " log the removal in the redo log "
        self logRemoveKey: aKey
            value: aValue
            term: pathTerm
            inCollisionBucket: collisionBkt
    ].

^ more
%

category: 'Error Handling'
method: RcIndexDictionary
_errorNoEntryForKey: aKey term: aTerm value: aValue

"An entry for the key/term/value was not present in the dictionary."

^ self
    _error: #rtErrIndexDictionaryEntryNotInDictionary
    args: #[ aKey, aTerm, aValue ]
%

category: 'Accessing'
method: RcIndexDictionary
at: aKey term: pathTerm otherwise: aValue

"Returns the value that corresponds to aKey/pathTerm.  If no such
 key/value/pathTerm triplet exists, returns aValue."

^ (self indexBucketFor: aKey) at: aKey term: pathTerm otherwise: aValue
%

category: 'Accessing'
method: RcIndexDictionary
firstAt: aKey term: pathTerm otherwise: aValue

"Returns the value that corresponds to aKey/pathTerm.  If no such
 key/value/pathTerm triplet exists, returns aValue.  If the value
 is a BucketValueBag, returns the first object in the Bag."

^ (self indexBucketFor: aKey) firstAt: aKey term: pathTerm otherwise: aValue
%

category: 'Accessing'
method: RcIndexDictionary
at: aKey term: pathTerm

"Returns the value that corresponds to aKey/pathTerm.  If no such
 key/value/pathTerm triplet exists, raise an error."

^ (self indexBucketFor: aKey) at: aKey term: pathTerm
%

category: 'Updating'
method: RcIndexDictionary
_existsAt: aKey put: aValue term: pathTerm logging: aBoolean

"Stores the aKey/aValue/pathTerm triplet in the index dictionary.  Rebuilds the
 hash table if the addition caused the number of collisions to exceed the limit
 allowed.  The argument aBoolean determines if an entry is placed in the redo
 log for this operation.  Returns whether the key/pathTerm pair already exists
 in the dictionary."

| collisionBkt exists |
collisionBkt := self indexBucketFor: aKey.
exists := collisionBkt
  at: aKey
  put: aValue
  term: pathTerm
  root: self
  logging: aBoolean.

aBoolean
    ifTrue: [
        self logAt: aKey put: aValue term: pathTerm inCollisionBucket: collisionBkt
    ].

^ exists
%

category: 'Query Support'
method: RcIndexDictionary
_findAllValuesNotIdenticalTo: aValue atTerm: pathTerm into: aCollection

"Iterate over all index buckets, finding all entries with the given path term.
 If the value at the path term is not identical to the given value, place it in
 the set."

| collBkt val |
1 to: self _basicSize do: [ :i |
    collBkt := self _at: i.
    1 to: collBkt numElements do: [ :j |
        ( ( (collBkt termAt: j) == pathTerm ) _and:
        [ (collBkt keyAt: j) ~~ aValue ] )
            ifTrue: [
                val := collBkt valueAt: j.
                (collBkt _isBucketValueBagAt: (collBkt valueOffsetFor: j))
                    ifTrue: [ aCollection addAll: val ]
                    ifFalse: [ aCollection add: val ]
            ]
    ]
].
^ aCollection
%

category: 'Query Support'
method: RcIndexDictionary
_findFirstKeyNotIdenticalTo: aKey atTerm: pathTerm

"Iterates over the index buckets, finding the first entry with the given path
 term.  If the key at the path term is not identical to the given key, returns
 it.  If all key are identical, returns nil."

| collBkt key |
1 to: self _basicSize do: [ :i |
    collBkt := self _at: i.
    1 to: collBkt numElements do: [ :j |
        ( ( (collBkt termAt: j) == pathTerm ) _and:
        [ (key := collBkt keyAt: j) ~~ aKey ] )
            ifTrue: [ ^ key ]
    ]
].
^ #_incompletePathTraversal
%

category: 'Accessing'
method: RcIndexDictionary
indexBucketFor: aKey

"Returns the RC index bucket for the given key."

<primitive: 528>
self _primitiveFailed: #indexBucketFor: .
self _uncontinuableError
%

category: 'Hashing'
method: RcIndexDictionary
hashFunction: aKey

"The hash function for aKey is calculated internally inside the method
 'indexBucketFor:'.  Raise a bogus error if this method is invoked."

self _error: #rtErrNilKey
%

category: 'Clustering'
method: RcIndexDictionary
clusterDepthFirst

"This method clusters the receiver and its collision buckets.
 Has no effect and returns true if the receiver was previously clustered
 in the current transaction, otherwise returns false."

self cluster
  ifTrue: [ ^ true ].

1 to: self _basicSize do: [ :i | (self _at: i) cluster ].
^ false
%

category: 'Statistics'
method: RcIndexDictionary
_statisticsInto: dict

"Puts statistical information into the given dictionary."

| pages bktCount count maxBkt maxBktSize collBkt numElemArr tableSizeArr 
  phySize systm |
pages := IdentitySet new.
numElemArr := Array new.
tableSizeArr := Array new.
phySize := self physicalSize.

bktCount := 0.
count :=0.
maxBktSize := 0.
systm := System .
systm _addEntireObjectToRcReadSet: self.

1 to: self _basicSize do: [ :i |
  collBkt := self _at: i.
  systm _addRootObjectToRcReadSet: collBkt.

  pages add: collBkt page.

  numElemArr add: collBkt numElements.
  tableSizeArr add: collBkt tableSize.

  collBkt size > 0
    ifTrue: [
      bktCount := bktCount + 1.
      count := count + collBkt size.
      (collBkt size > maxBktSize)
        ifTrue: [
          maxBktSize := collBkt size.
          maxBkt := collBkt
        ]
    ].

  phySize := phySize + collBkt physicalSize.
].

(bktCount > 0)
  ifTrue: [
    dict at: #AvgEntriesPerIndexBucket put: (count asFloat / bktCount) 
  ]
  ifFalse: [ dict at: #AvgEntriesPerIndexBucket put: 0].

dict at: #LargestIndexBucketSize
  put: (maxBkt == nil ifTrue: [ 0 ] ifFalse: [ maxBkt numElements ]).
dict at: #IndexDictionaryRootSize put: self _basicSize.
dict at: #IndexDictionaryPages put: pages.
dict at: #IndexDictionarySpaceUtilization put: #[ numElemArr, tableSizeArr ].
dict at: #IndexDictionaryPhysicalSize put: phySize.
%

category: 'Updating'
method: RcIndexDictionary
_refreshBucketCaches

"Verifies that the bucket caches are correct.  If there is an invalid cache
 entry, fixes it and reports any problems.  Returns a string."

| str result |
result := String new.
1 to: self _basicSize do: [ :i |
  str := String new.
  (self _at: i) _refreshBucketCacheReportingInto: str.
  str isEmpty
    ifFalse: [
      result add: 'For index bucket at '; add: i asString;
        addAll: str
    ]
].
^ result
%

! deleted _canonicalizeSymbolAt: offset oldSymbol: old newSymbol: newSymbol

category: 'Accessing'
method: RcIndexDictionary
okToUpdate

"Returns the value of the instance variable 'okToUpdate'."


^okToUpdate
%

category: 'Updating'
method: RcIndexDictionary
okToUpdate: newValue

"Modifies the value of the instance variable 'okToUpdate'."

okToUpdate := newValue
%

category: 'Accessing'
method: RcIndexDictionary
entryHolders

"Returns the value of the instance variable 'entryHolders'."

^entryHolders
%

category: 'Updating'
method: RcIndexDictionary
entryHolders: newValue

"Modifies the value of the instance variable 'entryHolders'."

entryHolders := newValue
%

category: 'Accessing'
method: RcIndexDictionary
getWorkingSetInterval: nsc

""

^ System
  rcValueCacheAt: #indexDictionaryWorkingSetInterval
  for: nsc
  otherwise: RcIndexDictionary defaultWorkingSetInterval.
%

category: 'Constants'
classmethod: RcIndexDictionary
defaultWorkingSetInterval

"Returns the default working set interval."

^ 100
%

category: 'Repository Conversion'
method: RcIndexDictionary
rehashForConversion

"Private. Rehashes the receiver because the hash values of some of its keys
 may have changed.

 Instances of RcIndexDictionary do not need to be rehashed."

^ self.
%

category: 'Hashing'
method: RcIndexDictionary
shouldRebuildTable: expectedGrowth

  | collBkt targetBucketSize incrementalGrowth |

targetBucketSize := RcIndexDictionary defaultBasicSize * 2 // (RcIndexBucket entrySize * 3).
incrementalGrowth := expectedGrowth // (self _basicSize).
1 to: self _basicSize do: [ :i |
  collBkt := self _at: i.
  ((collBkt size + incrementalGrowth) > targetBucketSize)
    ifTrue: [ ^true ].
].

^false
%

category: 'Audit'
method: RcIndexDictionary
auditKeysTermsAndValuesOn: aString do: aBlock

"Private. This method is currently unsupported, but 
 is provided for customer support."

System _addEntireObjectToRcReadSet: self.
self _doCollisionBuckets: [ :collisionBkt |
    1 to: collisionBkt tableSize do: [ :j | | aKey |
        aKey := collisionBkt keyAt: j.
        nil == aKey
            ifFalse: [ | aValue aTerm |
                aValue := collisionBkt valueAt: j.
                aTerm := collisionBkt termAt: j.
                (BucketValueBag _hasInstance: aValue)
                  ifTrue: [
                    (collisionBkt _isBucketValueBagAt: (collisionBkt valueOffsetFor: j))
                      ifFalse: [
                        aString add: 'IndexDictionary [';
                                   add: self asOop asString;
                                   add: '] has a BucketValueBag in the collision bucket [';
                                   add: collisionBkt asOop asString;
                                   add: '] at value offset ( ';
                                   add: (collisionBkt valueOffsetFor: j) asString;
                                   add: ' ) but the cached value indicates no BucketValueBag.';
                                   add: Character lf.
                      ].
                    aValue do: [:v |
                       aBlock value: aKey value: aTerm value: v.
                     ].
                  ]
                  ifFalse: [
                    (collisionBkt _isBucketValueBagAt: (collisionBkt valueOffsetFor: j))
                      ifTrue: [
                        aString add: 'IndexDictionary [';
                                   add: self asOop asString;
                                   add: '] has no BucketValueBag in the collision bucket [';
                                   add: collisionBkt asOop asString;
                                   add: '] at value offset ( ';
                                   add: (collisionBkt valueOffsetFor: j) asString;
                                   add: ' ) but the cached value indicate that a BucketValueBag should be present.';
                                   add: Character lf.
                      ].
                    aBlock value: aKey value: aTerm value: aValue.
                  ].
            ]
    ]
].
%

category: 'Audit'
method: RcIndexDictionary
auditNsc: nsc for: pathTerm on: aString

"Verifies that the elements in 'nsc' are correctly represented in
 the receiver.  This method is currently unsupported, but 
 is provided for customer support." 

| roots sz prevObj  |

roots := nsc _indexedPaths rootTerms.
sz := roots size.
prevObj := #_incompletePathTraversal.
nsc do: [ :obj | | rootTerm |
  obj ~~ prevObj
    ifTrue: [
      1 to: sz do: [ :j |
        rootTerm := roots at: j.
        rootTerm auditIndexDictionaryForObject:obj
          occurrences: (nsc occurrencesOf: obj)
          on: aString
      ]
    ].
  prevObj := obj.
].
%

category: 'Audit'
method: RcIndexDictionary
auditEntriesForNsc: nsc for: rootTerm on: aString


"Verifies that the indexDictionary structure is consistent relative to
 the given pathTerm.  This method is currently unsupported, but 
 is provided for customer support." 

self auditKeysTermsAndValuesOn: aString do: [:key :pathTerm :value |
  pathTerm indicatesIndexOnNscElements
    ifTrue: [
      (key == value)
        ifFalse: [
          pathTerm auditInfo: #[ key, nil, nil ] on: aString.
          aString add: 'IndexDictionary [';
            add: self asOop asString;
            add: '] has entry for [';
            add: value asOop asString;
            add: '] of class ';
            add: value class asString;
            add: ' associated with incorrect key [';
            add: key asOop asString;
            add: '] should be ( ';
            add:  value asOop asString;
            add: ') ';
            add: Character lf.
        ].
      (nsc includesIdentical: value)
        ifFalse: [
          aString add: 'Index Dictionary [';
                    add: self asOop asString;
                    add: '] has an entry [';
                    add: value asOop asString;
                    add: '] of class ';
                    add: value class asString;
                    add: ' that is not in the base collection';
                    add: Character lf
        ].
    ]
    ifFalse: [
        | ivOffset |
      ivOffset := (pathTerm _ivOffsetFor: value).
      ((value instVarAt: ivOffset) == key)
        ifFalse: [
          pathTerm auditInfo: #[ key, nil, nil ] on: aString.
          aString add: 'IndexDictionary [';
            add: self asOop asString;
            add: '] has entry for [';
            add: value asOop asString;
            add: '] of class ';
            add: value class asString;
            add: ' associated with incorrect key [';
            add: key asOop asString;
            add: '] should be ( ';
            add:  (value instVarAt: ivOffset) asOop asString;
            add: ') ';
            add: Character lf.
      ].
      (rootTerm == pathTerm) ifTrue: [
        (nsc includesIdentical: value)
          ifFalse: [
            aString add: 'Index Dictionary [';
                    add: self asOop asString;
                    add: '] has an entry [';
                    add: value asOop asString;
                    add: '] of class ';
                    add: value class asString;
                    add: ' that is not in the base collection';
                    add: Character lf
          ].
        ].
    ].
].

%

category: 'Accessing'
method: RcIndexDictionary
changeToSegment: aSegment

"Assign the receiver and its index buckets the given segment."

self assignToSegment: aSegment.
self _doCollisionBuckets: [ :indexBucket |
    indexBucket assignToSegment: aSegment
]
%

category: 'Private'
method: RcIndexDictionary
_validateRcConflictsWith: conflictObjects

"If the only conflict on this object is for the root, then we return true.
 Otherwise return false (to fail the transaction) if the redo log contains
 a removeKey: operation since we cannot guarantee success if the transaction
 did a removal from the RcIndexDictionary."

" if only had a physical conflict on the root "
( conflictObjects size = 1 _and: [ (conflictObjects at: 1) == self ] )
  ifTrue: [ ^ true ].

(self _checkLogEntriesForNoSelector: #_at:put:term:logging: )
  ifFalse: [ ^ false ].

^ self _checkLogEntriesForNoSelector: #_removeKey:value:logging: 
%
