Skip to content

Standardize Exception handling in souce plugins#6255

Merged
san81 merged 1 commit into
opensearch-project:mainfrom
vecheka:feature/standardize-exceptions
Nov 13, 2025
Merged

Standardize Exception handling in souce plugins#6255
san81 merged 1 commit into
opensearch-project:mainfrom
vecheka:feature/standardize-exceptions

Conversation

@vecheka

@vecheka vecheka commented Nov 11, 2025

Copy link
Copy Markdown
Contributor

Description

This is going to be a two part change to address exceptions handling for retrayble vs non-retryable exceptions.

Previous cancelled PR for more context: #6247

How

We are adding a new generic exception class CrawlerException to be shared by all connectors. This class is similar to previous API specific exception class (e.g Office365Exception).

CrawlerException will have two criterias:

  • Any REST API calls failures will be considered retryable. Additionally, writing to buffer will be considered retryable too.
  • Other exceptions (e.g internal failures) will be considered non-retryable

We will utilize CrawlerException, and throw this up all the way to WorkerSchedule where in the followup PR:

  • If the exception is flagged as "retryable = true", we will continue with current behaviour of backoff retry every 5ms
  • Otherwise, we will delay the retry by 1 day by calling sourceCoordinator.saveProgressStateForPartition(workerPartition, DURATION_TO_DELAY_RETRY). If it continues to fail up to 30 days (using partitionCreationTime field to confirm), we will give up the worker partition.

Is this change backward compatible?

Yes. We ensure to keep the catch block on generic "Exception" so all other connectors will still use the default behaviour of backoff retry every 5ms for all exception types.

Testing

Unit tests, ran the below successfully:

./gradlew :data-prepper-plugins:saas-source-plugins:source-crawler:test \
--tests "org.opensearch.dataprepper.plugins.source.source_crawler.exception.CrawlerExceptionTest"

./gradlew :data-prepper-plugins:saas-source-plugins:microsoft-office365-source:test \
--tests "org.opensearch.dataprepper.plugins.source.microsoft_office365.service.Office365ServiceTest"

./gradlew :data-prepper-plugins:saas-source-plugins:microsoft-office365-source:test \
--tests "org.opensearch.dataprepper.plugins.source.microsoft_office365.Office365CrawlerClientTest"

./gradlew :data-prepper-plugins:saas-source-plugins:microsoft-office365-source:test \
--tests "org.opensearch.dataprepper.plugins.source.microsoft_office365.Office365RestClientTest"

./gradlew :data-prepper-plugins:saas-source-plugins:source-crawler:test \
--tests "org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerSchedulerTest"

./gradlew :data-prepper-plugins:saas-source-plugins:microsoft-office365-source:checkstyleTest  
./gradlew :data-prepper-plugins:saas-source-plugins:source-crawler:checkstyleTest 

Local pipeline run succeeded:

2025-11-11T00:03:20,568 [main] INFO  org.opensearch.dataprepper.pipeline.parser.transformer.DynamicConfigTransformer - No transformation needed
2025-11-11T00:03:22,810 [main] INFO  org.opensearch.dataprepper.plugins.kafka.extension.KafkaClusterConfigExtension - Applying Kafka Cluster Config Extension.
2025-11-11T00:03:23,592 [main] INFO  org.opensearch.dataprepper.plugins.source.microsoft_office365.Office365Source - Creating Office365 Source Plugin
2025-11-11T00:03:23,660 [main] WARN  org.opensearch.dataprepper.core.pipeline.server.config.DataPrepperServerConfiguration - Creating data prepper server without authentication. This is not secure.
2025-11-11T00:03:23,660 [main] WARN  org.opensearch.dataprepper.core.pipeline.server.config.DataPrepperServerConfiguration - In order to set up Http Basic authentication for the data prepper server, go here: https://github.com/opensearch-project/data-prepper/blob/main/docs/core_apis.md#authentication
2025-11-11T00:03:24,780 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.microsoft_office365.Office365Source - Starting Office365 Source Plugin...
2025-11-11T00:03:24,781 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.auth.AuthenticationInterface - Initializing credentials.
2025-11-11T00:03:24,781 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.microsoft_office365.auth.Office365AuthenticationProvider - Getting new access token for Office 365 Management API
2025-11-11T00:03:24,786 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:03:24,787 [main] WARN  org.opensearch.dataprepper.core.pipeline.server.HttpServerProvider - Creating Data Prepper server without TLS. This is not secure.
2025-11-11T00:03:24,787 [main] WARN  org.opensearch.dataprepper.core.pipeline.server.HttpServerProvider - In order to set up TLS for the Data Prepper server, go here: https://github.com/opensearch-project/data-prepper/blob/main/docs/configuration.md#server-configuration
2025-11-11T00:03:24,895 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:03:24,895 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:03:24,991 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:03:26,102 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.microsoft_office365.auth.Office365AuthenticationProvider - Received new access token. Expires in 3599 seconds
2025-11-11T00:03:26,102 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.auth.AuthenticationInterface - Credentials initialized successfully
2025-11-11T00:03:26,102 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.microsoft_office365.Office365RestClient - Starting Office 365 subscriptions for audit logs
2025-11-11T00:03:27,307 [test-365-pipeline-sink-worker-2-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.CrawlerSourcePlugin - Starting microsoft_office365 Source Plugin
2025-11-11T00:03:27,515 [pool-6-thread-3] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Worker thread started
2025-11-11T00:03:27,515 [pool-6-thread-3] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Processing Partitions
2025-11-11T00:03:27,514 [pool-6-thread-2] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Worker thread started
2025-11-11T00:03:27,516 [pool-6-thread-2] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Processing Partitions
2025-11-11T00:03:27,517 [pool-6-thread-4] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Worker thread started
2025-11-11T00:03:27,517 [pool-6-thread-5] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Worker thread started
2025-11-11T00:03:27,517 [pool-6-thread-4] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Processing Partitions
2025-11-11T00:03:27,517 [pool-6-thread-5] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.WorkerScheduler - Processing Partitions
2025-11-11T00:03:28,005 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler.LeaderScheduler - Running as a LEADER node
2025-11-11T00:03:28,449 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:04:26,810 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:04:27,079 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:04:28,951 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:05:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:05:26,906 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:05:29,441 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:06:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:06:26,911 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:06:29,923 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:07:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:07:26,905 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:07:30,402 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:08:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:08:26,905 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:08:30,907 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:09:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:09:28,593 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:09:31,407 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:10:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:10:26,899 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:10:31,907 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0
2025-11-11T00:11:26,809 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Retrieving latest secrets in aws:secrets:office365-credentials.
2025-11-11T00:11:26,904 [pool-3-thread-1] INFO  org.opensearch.dataprepper.plugins.aws.AwsSecretsSupplier - Finished retrieving latest secret in aws:secrets:office365-credentials.
2025-11-11T00:11:32,392 [pool-6-thread-1] INFO  org.opensearch.dataprepper.plugins.source.source_crawler.base.DimensionalTimeSliceCrawler - Total partitions created in this crawl: 0.0

Issues Resolved

N/A

Check List

  • New functionality includes testing.
  • New functionality has a documentation issue. Please link to it in this PR.
    • New functionality has javadoc added
  • Commits are signed with a real name per the DCO

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

import org.opensearch.dataprepper.metrics.PluginMetrics;
import org.opensearch.dataprepper.model.record.Record;
import org.opensearch.dataprepper.plugins.source.microsoft_office365.exception.Office365Exception;
import org.opensearch.dataprepper.plugins.source.source_crawler.exception.CrawlerException;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a new revision. Suggest to change to SaasCrawlerException

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I like the suggested name as well. Will update to address your other comments here as well. Thank you!

requestErrorsCounter.increment();
throw e;
if (e instanceof CrawlerException) {
throw (CrawlerException) e;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to cast it to CrawlerException? I think it is safe to just throw e

break;
}
}
} catch (CrawlerException e) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocker. If there is a newer revision, we can remove line 97-99 to avoid confusion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Can see why it could cause confusion until we actually implement the retry strategy for non-retryable exception.

@vecheka vecheka force-pushed the feature/standardize-exceptions branch 2 times, most recently from 7226b33 to 68eee67 Compare November 11, 2025 05:15
logType, startTime, endTime, e);
requestErrorsCounter.increment();
throw e;
if (e instanceof SaaSCrawlerException) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased from this PR: #6238

Signed-off-by: Vecheka Chhourn <vecheka@amazon.com>
@vecheka vecheka force-pushed the feature/standardize-exceptions branch from 68eee67 to c3f0349 Compare November 12, 2025 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants