Skip to content

Make pystac handle item creation asynchronously #609

Closed as not planned
Closed as not planned
@xenon-dev

Description

@xenon-dev

As of version 1.1.0 stac items are handeled in a synchronized way. This is fine for a few items, but has a long runtime with over hundreds of items.

I use our own LGLN script to build the catalog up from collections and items, calling our own function for the heavy lifting, normalizing and then save the catalog in the end.

With pythons concurrent lib, I was able to fasten up the item creation from tiff files with the ThreadPoolExecutor, but having trouble with using a ProcessPoolExecutor, which would be the right way to do it, cause the task is pretty much just cpu-bound, not I/O-bound in any way.

I can see a 400% increase in item creation speed with 16 processes spawned instead of one, but have to comment out the save method in the end, which then generated nothing.

Unfortunately, the catalog.save method does not wait at all for the catalog.add_item method to return and so tries to build a dictionary from an empty list, which results in an error.

To circumvent this, it would be really great, if pystac itself internally could handle the creation of items and adding to the catalog asynchronously, maybe with an extra parameter, to be synchronious handling still be the default, because "it just works".

def addImageItemsToCatalog(imageList, catalog):
    """multithreaded execution inspired by:
    https://stackoverflow.com/questions/21143162/python-wait-on-all-of-concurrent-futures-threadpoolexecutors-futures
    Performance increase was measured to be around 100%, measured with 93 stac items in 2 different catalogs,
    compared to singlethreaded.
    Could be further increased with multiprocess implementation (e.g. use "ProcessPoolExecutor" instead),
    but a way to have the "catalog.save" method be waiting on the execution has to be found, otherwise
    pyStac will fail.
    """
    executor = concurrent.futures.ProcessPoolExecutor(MULTI_THREAD_SWEET_SPOT) 
    futures = [executor.submit(writeImageFileItems, imageFile, catalog) for imageFile in imageList]
    concurrent.futures.wait(futures, timeout=None, return_when=ALL_COMPLETED)
    # VVV this is done on the main thread, without waiting, which crashes :(
    catalog.normalize_hrefs("generated/dop")
    print("Saving catalog to " + catalog.get_self_href())
    catalog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)

The resulting error would be the following, which I think is clear to me, because .save() does not wait for anything to be in there:

Traceback (most recent call last):
  File "C:\dev\repo\task-3d-2.0\prototypes\dopCatalogBuilder\main.py", line 9, in <module>
    main()
  File "C:\dev\repo\task-3d-2.0\prototypes\dopCatalogBuilder\main.py", line 5, in main
    dopCatalogBuilder.build()
  File "C:\dev\repo\task-3d-2.0\prototypes\dopCatalogBuilder\dopCatalogBuilder.py", line 100, in build
    dop_catalog = createCatalogFromAreaDictionary(dopImages, subcatalogs, catalogTitle="test raster Orthophotos",
  File "C:\dev\repo\task-3d-2.0\prototypes\dopCatalogBuilder\dopCatalogBuilder.py", line 114, in createCatalogFromAreaDictionary
    addImageItemsToCatalog(images, dopCatalog)
  File "C:\dev\repo\task-3d-2.0\prototypes\dopCatalogBuilder\dopCatalogBuilder.py", line 133, in addImageItemsToCatalog
    catalog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)
  File "C:\Users\dev\anaconda3\envs\catalogBuilder\lib\site-packages\pystac\catalog.py", line 753, in save
    child.save()
  File "C:\Users\dev\anaconda3\envs\catalogBuilder\lib\site-packages\pystac\catalog.py", line 783, in save
    self.save_object(
  File "C:\Users\dev\anaconda3\envs\catalogBuilder\lib\site-packages\pystac\stac_object.py", line 341, in save_object
    stac_io.save_json(dest_href, self.to_dict(include_self_link=include_self_link))
  File "C:\Users\dev\anaconda3\envs\catalogBuilder\lib\site-packages\pystac\collection.py", line 520, in to_dict
    d["extent"] = self.extent.to_dict()
AttributeError: 'list' object has no attribute 'to_dict'

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions