Xyna-Factory / xyna-factory

Sources of the Xyna Factory Server, Xyna runtime applications (like GuiHttp or gitintegration), and installation scripts.
Apache License 2.0
14 stars 3 forks source link

Storing Storables with many listentries takes quite some time #145

Closed csc-gip closed 1 year ago

csc-gip commented 1 year ago

https://github.com/GIP-SmartMercial/xyna-factory/blob/7d5e3053c6c0e73aba4cc7cf17b2ff2d1824d0d3/server/src/com/gip/xyna/xnwh/persistence/xmom/XMOMPersistenceOperationAlgorithms.java#L2311

If you have a Storable containing a reference to a Storable as list and you store a lot of elements this gets expensive. Storing 160k elements takes about 15 minutes and Xyna spends a lot of its time in this method.

Using a hashtable with the primary key as key instead of a list in https://github.com/GIP-SmartMercial/xyna-factory/blob/7d5e3053c6c0e73aba4cc7cf17b2ff2d1824d0d3/server/src/com/gip/xyna/xnwh/persistence/xmom/XMOMPersistenceOperationAlgorithms.java#L2294 and using containsKey in https://github.com/GIP-SmartMercial/xyna-factory/blob/7d5e3053c6c0e73aba4cc7cf17b2ff2d1824d0d3/server/src/com/gip/xyna/xnwh/persistence/xmom/XMOMPersistenceOperationAlgorithms.java#L2312 should help.

csc-gip commented 1 year ago

Possible implementation

  private static class PersistenceStoreContext {

    private Map<String, HashMap<String, Storable<?>>> objectsToStorePerTable = new HashMap<>();
    private Map<Storable<?>, XynaObject> collisionInformation = new HashMap<>();
    private final List<StorableStructureInformation> order;

    private PersistenceStoreContext(XMOMStorableStructureInformation rootInfo) {
      order = getStorableStructureHierarchyOrder(rootInfo);
    }

    public boolean add(StorableStructureInformation info, Storable<?> storable, XynaObject xoStorable) {
      if (storable == null) {
         return false;
      }

      Map<String, Storable<?>> objectsToStoreForTable = objectsToStorePerTable.get(info.getTableName());
      if (objectsToStoreForTable == null) {
        objectsToStoreForTable = new HashMap<String, Storable<?>>();
        objectsToStorePerTable.put(info.getTableName(), objectsToStoreForTable);
      }

      String pk = String.valueOf(storable.getPrimaryKey());
      boolean contained = objectsToStoreForTable.containsKey(pk);

      if (!contained) {
        objectsToStoreForTable.put(pk, storable);
        if (info instanceof XMOMStorableStructureInformation) {
          collisionInformation.put(storable, xoStorable);
        }
      } else {
        logger.debug("Multiple instances for table " + info.getTableName() + " with same primaryKey " + pk + " contained in hierarchy, skipping additional Data!");
      }

      return contained;
    }

    public void store(ODSConnection con) throws HistorizationTimeStampCollision, PersistenceLayerException {
      try {
        for (StorableStructureInformation structureInfo : order) {
          List<Storable<?>> objectsToStoreForTable = objectsToStorePerTable.remove(structureInfo.getTableName());
          if (objectsToStoreForTable != null && !objectsToStoreForTable.isEmpty()) {
            if (structureInfo instanceof XMOMStorableStructureInformation) {
              for (Storable<?> rootOrReferenceStorable : objectsToStoreForTable.values()) {
                if (con.persistObject(rootOrReferenceStorable)) {
                  // update-fall
                  // can happen if the entry did not exist during historization but does now exist
                  throw new HistorizationTimeStampCollision((XMOMStorableStructureInformation)structureInfo, collisionInformation.get(rootOrReferenceStorable));
                }
              }
            } else {
              if (!objectsToStoreForTable.isEmpty()) {
                for (Storable<?> objectToStoreForTable : objectsToStoreForTable.values()) {
                  if (objectToStoreForTable != null) {
                    con.persistObject(objectToStoreForTable);
                  }
                }
              }        
            }
          }
        }
      } finally {
        // there might be a pending retry
        objectsToStorePerTable.clear();
        collisionInformation.clear();
      }
    }

  }