DSpace-CRIS 8: error `noSetHierarchy` al cosechar un repositorio OAI-PMH sin Sets

Por Staff INFODI
12 minutos
DSpace-CRIS 8: error `noSetHierarchy` al cosechar un repositorio OAI-PMH sin Sets

Durante una revisión de harvesting en DSpace-CRIS 8 (2024.02.03 / base DSpace 8.0.1) nos encontramos con un comportamiento interesante al configurar una colección para recolectar metadatos desde un proveedor OAI-PMH externo.

El escenario era el siguiente:

  • DSpace-CRIS 8.
  • Recolección mediante OAI-PMH.
  • Formato de metadatos: Simple Dublin Core (dc / oai_dc).
  • Recolección de todos los registros del proveedor.
  • El proveedor OAI-PMH no implementa Sets.
  • La configuración visual de la colección no indicaba ningún Set OAI específico.

Al ejecutar la recolección, DSpace-CRIS terminaba en estado RETRY y mostraba:

Not recoverable error occurs:
OAI server response contains the following error codes:
[noSetHierarchy]

¿Qué significa noSetHierarchy?

Para comprobar el comportamiento del proveedor realizamos las consultas OAI-PMH directamente.

Por ejemplo:

curl -sv 'https://servidor/oai?verb=Identify'

La operación Identify respondía correctamente.

También verificamos:

curl -sv 'https://servidor/oai?verb=ListRecords&metadataPrefix=oai_dc'

y el proveedor entregaba los registros correctamente.

Sin embargo:

curl -sv 'https://servidor/oai?verb=ListSets'

respondía:

<error code="noSetHierarchy">
    This repository does not support sets
</error>

Esto, por sí mismo, no constituye una falla del proveedor OAI-PMH. Simplemente significa que el repositorio no organiza sus registros mediante Sets OAI.

Si queremos cosechar todos sus registros, no necesitamos especificar un Set.

La pista clave: dos DSpace-CRIS 8 con comportamientos diferentes

Para diagnosticar el problema comparamos dos instalaciones de desarrollo de DSpace-CRIS 8 con prácticamente la misma configuración.

En el primer ambiente, la recolección funcionaba correctamente:

Estado: READY
Imported 18 records with success

En el segundo ambiente, utilizando el mismo proveedor OAI y el mismo formato de metadatos, obteníamos:

Not recoverable error occurs:
OAI server response contains the following error codes:
[noSetHierarchy]

Inicialmente verificamos si existían diferencias de versión.

En ambos servidores encontramos:

dspace-api-lang-8.0.1.jar
dspace-api-cris-2024.02.03.jar

Por lo tanto, no estábamos simplemente ante dos versiones diferentes de DSpace-CRIS.

También probamos directamente las operaciones OAI-PMH desde ambos servidores.

El proveedor respondía correctamente a Identify y ListRecords, mientras que ListSets devolvía noSetHierarchy, como correspondía a un repositorio que no implementa Sets.

La diferencia, por lo tanto, debía estar en otro lugar.

Revisando PostgreSQL

DSpace mantiene la configuración de harvesting de las colecciones en la tabla:

harvested_collection

Podemos localizarla con:

sudo -u postgres psql -d dspace -c "\dt *harvest*"

En nuestro caso aparecieron:

harvested_collection
harvested_item

La estructura de harvested_collection incluye, entre otros, los campos:

harvest_type
oai_source
oai_set_id
harvest_message
metadata_config_id
harvest_status
harvest_start_time
last_harvested
collection_id

Al comparar los registros encontramos finalmente la diferencia.

En el ambiente que funcionaba correctamente:

oai_source         = https://servidor/oai
oai_set_id         = ''
metadata_config_id = dc
harvest_type       = 1
harvest_status     = 0

Mientras que en el ambiente que fallaba:

oai_source         = https://servidor/oai
oai_set_id         = all
metadata_config_id = dc
harvest_type       = 1
harvest_status     = 4

El mensaje asociado era:

Not recoverable error occurs:
OAI server response contains the following error codes:
[noSetHierarchy]

NULL, '' y 'all' no son equivalentes

Esta terminó siendo la parte más importante del diagnóstico.

Probamos inicialmente reemplazar all por NULL.

El resultado fue otro error:

Not recoverable error occurs:
Provided collection is not set up for harvesting

Por lo tanto, NULL tampoco representaba correctamente el escenario.

Para comprobar exactamente qué contenía el ambiente funcional ejecutamos:

SELECT
    id,
    quote_nullable(oai_set_id) AS valor,
    length(oai_set_id) AS longitud,
    octet_length(oai_set_id) AS bytes,
    encode(convert_to(oai_set_id,'UTF8'),'hex') AS hex
FROM harvested_collection;

En el DSpace-CRIS que funcionaba obtuvimos:

valor | longitud | bytes | hex
------+----------+-------+-----
''    |        0 |     0 |

Es decir, no era NULL.

Era una cadena vacía real.

En cambio, en el ambiente con problemas:

valor | longitud | bytes | hex
------+----------+-------+--------
'all' |        3 |     3 | 616c6c

Con esto pudimos identificar claramente tres comportamientos diferentes:

oai_set_id = NULL
→ DSpace considera que la colección no está correctamente
  configurada para harvesting.

oai_set_id = 'all'
→ se produce una operación relacionada con Sets.
→ el proveedor responde noSetHierarchy.
→ la recolección falla.

oai_set_id = ''
→ no existe un Set OAI específico.
→ DSpace puede recolectar el repositorio completo.

Corrección

Para dejar la colección problemática con el mismo comportamiento del ambiente funcional, corregimos oai_set_id utilizando una cadena vacía:

UPDATE harvested_collection
SET oai_set_id = '',
    harvest_status = 0,
    harvest_message = NULL,
    harvest_start_time = NULL
WHERE id = 1;

Posteriormente verificamos:

SELECT
    id,
    oai_source,
    quote_nullable(oai_set_id) AS oai_set_id,
    length(oai_set_id) AS longitud,
    metadata_config_id,
    harvest_type,
    harvest_status,
    harvest_message
FROM harvested_collection;

Para una colección que debe recolectar todo el repositorio y cuyo proveedor no utiliza Sets, el resultado esperado es:

oai_set_id = ''
length     = 0

Una consideración importante sobre la interfaz

Este caso también demuestra por qué no siempre basta con revisar la interfaz administrativa.

Visualmente, el campo:

ID de set específico de OAI

podía aparecer vacío.

Sin embargo, en PostgreSQL el ambiente problemático contenía:

'all'

mientras que el ambiente funcional contenía:

''

Para el administrador ambos escenarios pueden parecer iguales desde la interfaz, pero para el backend son valores diferentes y pueden producir comportamientos completamente distintos.

Por eso, cuando un harvesting OAI-PMH presenta errores aparentemente inconsistentes, resulta útil revisar directamente:

harvested_collection.oai_source
harvested_collection.oai_set_id
harvested_collection.metadata_config_id
harvested_collection.harvest_type
harvested_collection.harvest_status
harvested_collection.harvest_message

También apareció un segundo problema: SMTP

Durante el diagnóstico encontramos además un error como:

Couldn't connect to host, port:
smtp.example.com, 25

seguido de:

java.net.UnknownHostException: smtp.example.com

Este error no era la causa original del fallo OAI-PMH.

La secuencia era:

Harvesting falla
        ↓
DSpace intenta notificar el fallo
        ↓
OAIHarvesterEmailSender intenta enviar correo
        ↓
SMTP sigue configurado como smtp.example.com
        ↓
también falla la notificación

Es importante separar ambos problemas, porque un stacktrace extenso relacionado con SMTP puede llevar a pensar que el harvesting está fallando por correo, cuando en realidad el correo solamente está intentando informar de un error producido anteriormente.

Conclusión

Cuando DSpace-CRIS 8 devuelve:

OAI server response contains the following error codes:
[noSetHierarchy]

no debemos asumir inmediatamente que el proveedor OAI-PMH está defectuoso.

Primero conviene comprobar:

curl 'https://servidor/oai?verb=Identify'

curl 'https://servidor/oai?verb=ListSets'

curl 'https://servidor/oai?verb=ListRecords&metadataPrefix=oai_dc'

Si Identify y ListRecords funcionan, pero ListSets responde:

noSetHierarchy

el proveedor simplemente puede no implementar Sets.

En DSpace-CRIS 8 también es recomendable inspeccionar directamente harvested_collection y, particularmente, distinguir entre:

NULL
''
'all'

porque no necesariamente representan el mismo estado para el harvesting.

En nuestro caso, la comparación entre dos instalaciones DSpace-CRIS 8 permitió aislar la diferencia:

Ambiente funcional
oai_set_id = ''

Ambiente con error
oai_set_id = 'all'

Una diferencia de apenas tres caracteres que desde la interfaz prácticamente no era visible, pero que terminaba determinando si la cosecha OAI-PMH podía completarse o finalizaba con noSetHierarchy.

Este tipo de diagnóstico comparativo entre ambientes equivalentes resulta especialmente útil en DSpace: antes de atribuir el problema al proveedor OAI, a la red, a Cloudflare o a la versión de DSpace, vale la pena comparar el estado real persistido en PostgreSQL.