Skip to main content

ODP Subscription Management

This advanced guide covers comprehensive ODP subscription management using ERPL. Learn how to manage subscription lifecycles, handle cursors, implement recovery strategies, and optimize multi-subscriber patterns.

For SAP Administrators and Data Engineers

This guide is designed for SAP Basis administrators, data engineers, and ETL developers who need to manage complex ODP replication scenarios.

Understanding ODP Subscriptions

ODP subscriptions manage the state of data replication between SAP systems and external consumers. Each subscription tracks:

  • Cursor Position - Last successfully extracted record
  • Subscription Status - Active, Error, Suspended
  • Change Tracking - Insert/Update/Delete operations
  • Recovery Information - Failed extraction details

Subscription Lifecycle Management

Creating Subscriptions

-- Create subscription with full load
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'FULL',
subscriber_name => 'ERPL',
subscriber_type => 'SAP_BW'
);

-- Verify subscription creation
SELECT * FROM sap_odp_show_subscriptions()
WHERE subscriber_name = 'ERPL' AND data_source = 'VBAK$F';

Monitoring Subscription Status

-- Check all subscriptions
SELECT * FROM sap_odp_show_subscriptions();

-- Monitor specific subscription
SELECT
subscriber_name,
subscriber_type,
data_source,
context,
status,
created_date,
last_used_date,
error_message
FROM sap_odp_show_subscriptions()
WHERE subscriber_name = 'ERPL'
ORDER BY last_used_date DESC;

Subscription Health Check

-- Comprehensive subscription health check
WITH subscription_health AS (
SELECT
subscriber_name,
data_source,
context,
status,
CASE
WHEN status = 'ACTIVE' THEN 'HEALTHY'
WHEN status = 'ERROR' THEN 'NEEDS_ATTENTION'
WHEN status = 'SUSPENDED' THEN 'SUSPENDED'
ELSE 'UNKNOWN'
END AS health_status,
last_used_date,
CURRENT_DATE - last_used_date AS days_since_last_use
FROM sap_odp_show_subscriptions()
)
SELECT
subscriber_name,
data_source,
health_status,
days_since_last_use,
CASE
WHEN days_since_last_use > 7 THEN 'STALE'
WHEN days_since_last_use > 1 THEN 'RECENT'
ELSE 'CURRENT'
END AS usage_status
FROM subscription_health
ORDER BY days_since_last_use DESC;

Cursor Management

Understanding Cursors

Cursors track the position of the last successfully extracted record. They are essential for delta replication:

-- View all cursors
SELECT * FROM sap_odp_show_cursors();

-- Check cursor for specific replication mode
SELECT * FROM sap_odp_show_cursors(
replication_mode => 'DELTA'
);

-- Detailed cursor analysis
SELECT
subscriber_name,
data_source,
context,
replication_mode,
cursor_position,
last_extraction_date,
records_extracted,
status
FROM sap_odp_show_cursors()
WHERE subscriber_name = 'ERPL'
ORDER BY last_extraction_date DESC;

Cursor Position Analysis

-- Analyze cursor positions across subscriptions
WITH cursor_analysis AS (
SELECT
data_source,
replication_mode,
COUNT(*) AS cursor_count,
MIN(cursor_position) AS min_position,
MAX(cursor_position) AS max_position,
AVG(cursor_position) AS avg_position
FROM sap_odp_show_cursors()
GROUP BY data_source, replication_mode
)
SELECT
data_source,
replication_mode,
cursor_count,
min_position,
max_position,
avg_position,
max_position - min_position AS position_range
FROM cursor_analysis
ORDER BY position_range DESC;

Recovery Strategies

Error Detection and Analysis

-- Identify failed subscriptions
SELECT * FROM sap_odp_show_subscriptions()
WHERE status = 'ERROR';

-- Analyze error patterns
WITH error_analysis AS (
SELECT
data_source,
context,
error_message,
COUNT(*) AS error_count,
MAX(last_used_date) AS last_error_date
FROM sap_odp_show_subscriptions()
WHERE status = 'ERROR'
GROUP BY data_source, context, error_message
)
SELECT
data_source,
context,
error_message,
error_count,
last_error_date,
CASE
WHEN error_count > 5 THEN 'FREQUENT_ERRORS'
WHEN error_count > 2 THEN 'OCCASIONAL_ERRORS'
ELSE 'RARE_ERRORS'
END AS error_frequency
FROM error_analysis
ORDER BY error_count DESC;

Recovery Procedures

-- Standard recovery procedure
-- 1. Check subscription status
SELECT * FROM sap_odp_show_subscriptions()
WHERE subscriber_name = 'ERPL' AND data_source = 'VBAK$F';

-- 2. Check cursor status
SELECT * FROM sap_odp_show_cursors()
WHERE subscriber_name = 'ERPL' AND data_source = 'VBAK$F';

-- 3. Attempt recovery
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'RECOVER'
);

-- 4. Verify recovery success
SELECT * FROM sap_odp_show_subscriptions()
WHERE subscriber_name = 'ERPL' AND data_source = 'VBAK$F';

Automated Recovery Script

-- Automated recovery for all failed subscriptions
WITH failed_subscriptions AS (
SELECT
subscriber_name,
data_source,
context,
error_message
FROM sap_odp_show_subscriptions()
WHERE status = 'ERROR'
),
recovery_attempts AS (
SELECT
subscriber_name,
data_source,
context,
CASE
WHEN error_message LIKE '%TIMEOUT%' THEN 'RECOVER'
WHEN error_message LIKE '%CONNECTION%' THEN 'RECOVER'
ELSE 'FULL'
END AS recovery_mode
FROM failed_subscriptions
)
SELECT
subscriber_name,
data_source,
context,
recovery_mode,
CONCAT(
'SELECT * FROM sap_odp_read_full(''',
context, ''', ''',
data_source, ''',
replication_mode => ''', recovery_mode, ''',
subscriber_name => ''', subscriber_name, ''');'
) AS recovery_sql
FROM recovery_attempts;

Multi-Subscriber Patterns

Managing Multiple Subscribers

-- Create multiple subscribers for same data source
-- Subscriber 1: Real-time processing
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'FULL',
subscriber_name => 'REALTIME_PROCESSOR',
subscriber_type => 'SAP_BW'
);

-- Subscriber 2: Batch processing
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'FULL',
subscriber_name => 'BATCH_PROCESSOR',
subscriber_type => 'SAP_BW'
);

-- Subscriber 3: Analytics
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'FULL',
subscriber_name => 'ANALYTICS_ENGINE',
subscriber_type => 'SAP_BW'
);

Subscriber Coordination

-- Monitor all subscribers for a data source
SELECT
subscriber_name,
subscriber_type,
status,
last_used_date,
CASE
WHEN last_used_date = CURRENT_DATE THEN 'CURRENT'
WHEN last_used_date = CURRENT_DATE - 1 THEN 'YESTERDAY'
ELSE 'STALE'
END AS usage_status
FROM sap_odp_show_subscriptions()
WHERE data_source = 'VBAK$F'
ORDER BY last_used_date DESC;

-- Check subscriber synchronization
WITH subscriber_sync AS (
SELECT
data_source,
COUNT(DISTINCT subscriber_name) AS subscriber_count,
COUNT(CASE WHEN status = 'ACTIVE' THEN 1 END) AS active_count,
COUNT(CASE WHEN status = 'ERROR' THEN 1 END) AS error_count,
MIN(last_used_date) AS earliest_use,
MAX(last_used_date) AS latest_use
FROM sap_odp_show_subscriptions()
GROUP BY data_source
)
SELECT
data_source,
subscriber_count,
active_count,
error_count,
earliest_use,
latest_use,
latest_use - earliest_use AS sync_delay_days
FROM subscriber_sync
ORDER BY sync_delay_days DESC;

Performance Optimization

Subscription Performance Analysis

-- Analyze subscription performance
WITH performance_analysis AS (
SELECT
subscriber_name,
data_source,
context,
last_used_date,
LAG(last_used_date) OVER (
PARTITION BY subscriber_name, data_source
ORDER BY last_used_date
) AS previous_use_date,
last_used_date - LAG(last_used_date) OVER (
PARTITION BY subscriber_name, data_source
ORDER BY last_used_date
) AS extraction_interval
FROM sap_odp_show_subscriptions()
WHERE status = 'ACTIVE'
)
SELECT
subscriber_name,
data_source,
AVG(extraction_interval) AS avg_interval_hours,
MIN(extraction_interval) AS min_interval_hours,
MAX(extraction_interval) AS max_interval_hours,
STDDEV(extraction_interval) AS interval_stddev
FROM performance_analysis
WHERE extraction_interval IS NOT NULL
GROUP BY subscriber_name, data_source
ORDER BY avg_interval_hours DESC;

Cursor Performance Optimization

-- Optimize cursor management
WITH cursor_performance AS (
SELECT
subscriber_name,
data_source,
replication_mode,
records_extracted,
last_extraction_date,
CASE
WHEN records_extracted > 100000 THEN 'LARGE_BATCH'
WHEN records_extracted > 10000 THEN 'MEDIUM_BATCH'
ELSE 'SMALL_BATCH'
END AS batch_size_category
FROM sap_odp_show_cursors()
)
SELECT
batch_size_category,
COUNT(*) AS cursor_count,
AVG(records_extracted) AS avg_records,
MIN(records_extracted) AS min_records,
MAX(records_extracted) AS max_records
FROM cursor_performance
GROUP BY batch_size_category
ORDER BY avg_records DESC;

Advanced Patterns

Subscription Cleanup

-- Identify stale subscriptions for cleanup
WITH stale_subscriptions AS (
SELECT
subscriber_name,
data_source,
context,
last_used_date,
CURRENT_DATE - last_used_date AS days_inactive
FROM sap_odp_show_subscriptions()
WHERE status = 'ACTIVE'
),
cleanup_candidates AS (
SELECT
subscriber_name,
data_source,
context,
days_inactive,
CASE
WHEN days_inactive > 30 THEN 'DROP'
WHEN days_inactive > 7 THEN 'SUSPEND'
ELSE 'KEEP'
END AS cleanup_action
FROM stale_subscriptions
)
SELECT
subscriber_name,
data_source,
context,
days_inactive,
cleanup_action,
CASE
WHEN cleanup_action = 'DROP' THEN
CONCAT('PRAGMA sap_odp_drop(''', context, ''', ''', data_source, ''', ''', subscriber_name, ''');')
WHEN cleanup_action = 'SUSPEND' THEN
CONCAT('-- Consider suspending: ', subscriber_name, ' for ', data_source)
ELSE '-- Keep active'
END AS cleanup_sql
FROM cleanup_candidates
ORDER BY days_inactive DESC;

Disaster Recovery

-- Disaster recovery procedure
-- 1. Identify all active subscriptions
WITH active_subscriptions AS (
SELECT
subscriber_name,
data_source,
context,
last_used_date
FROM sap_odp_show_subscriptions()
WHERE status = 'ACTIVE'
),
-- 2. Generate recovery commands
recovery_commands AS (
SELECT
subscriber_name,
data_source,
context,
CONCAT(
'-- Recover subscription for ', subscriber_name, '\n',
'SELECT * FROM sap_odp_read_full(''',
context, ''', ''',
data_source, ''',
replication_mode => ''RECOVER'',
subscriber_name => ''', subscriber_name, ''');'
) AS recovery_command
FROM active_subscriptions
)
SELECT
subscriber_name,
data_source,
context,
recovery_command
FROM recovery_commands
ORDER BY subscriber_name, data_source;

Monitoring and Alerting

Subscription Monitoring Dashboard

-- Create monitoring dashboard data
WITH monitoring_data AS (
SELECT
subscriber_name,
data_source,
context,
status,
last_used_date,
CURRENT_DATE - last_used_date AS days_since_last_use,
CASE
WHEN status = 'ERROR' THEN 'CRITICAL'
WHEN CURRENT_DATE - last_used_date > 1 THEN 'WARNING'
WHEN CURRENT_DATE - last_used_date > 0 THEN 'INFO'
ELSE 'OK'
END AS alert_level
FROM sap_odp_show_subscriptions()
)
SELECT
alert_level,
COUNT(*) AS subscription_count,
STRING_AGG(CONCAT(subscriber_name, ':', data_source), ', ') AS affected_subscriptions
FROM monitoring_data
GROUP BY alert_level
ORDER BY
CASE alert_level
WHEN 'CRITICAL' THEN 1
WHEN 'WARNING' THEN 2
WHEN 'INFO' THEN 3
ELSE 4
END;

Automated Health Checks

-- Automated health check procedure
WITH health_check AS (
SELECT
COUNT(CASE WHEN status = 'ERROR' THEN 1 END) AS error_count,
COUNT(CASE WHEN status = 'ACTIVE' THEN 1 END) AS active_count,
COUNT(CASE WHEN CURRENT_DATE - last_used_date > 1 THEN 1 END) AS stale_count,
COUNT(*) AS total_subscriptions
FROM sap_odp_show_subscriptions()
)
SELECT
total_subscriptions,
active_count,
error_count,
stale_count,
ROUND(active_count * 100.0 / total_subscriptions, 2) AS health_percentage,
CASE
WHEN error_count > 0 THEN 'UNHEALTHY'
WHEN stale_count > total_subscriptions * 0.1 THEN 'DEGRADED'
ELSE 'HEALTHY'
END AS overall_health
FROM health_check;

Best Practices

Subscription Naming Conventions

-- Recommended naming patterns
-- Environment_Purpose_System
-- Examples:
-- PROD_ETL_SALES
-- DEV_ANALYTICS_FINANCE
-- TEST_REPORTING_HR

-- Validate naming conventions
SELECT
subscriber_name,
CASE
WHEN subscriber_name LIKE '%_%_%' THEN 'GOOD_FORMAT'
ELSE 'NEEDS_REDESIGN'
END AS naming_convention,
LENGTH(subscriber_name) AS name_length
FROM sap_odp_show_subscriptions()
GROUP BY subscriber_name
ORDER BY naming_convention, name_length;

Subscription Lifecycle Automation

-- Automated subscription lifecycle management
WITH lifecycle_management AS (
SELECT
subscriber_name,
data_source,
context,
status,
last_used_date,
CASE
WHEN status = 'ERROR' AND CURRENT_DATE - last_used_date > 1 THEN 'RECOVER'
WHEN status = 'ACTIVE' AND CURRENT_DATE - last_used_date > 7 THEN 'SUSPEND'
WHEN status = 'SUSPENDED' AND CURRENT_DATE - last_used_date > 30 THEN 'DROP'
ELSE 'MAINTAIN'
END AS lifecycle_action
FROM sap_odp_show_subscriptions()
)
SELECT
subscriber_name,
data_source,
lifecycle_action,
COUNT(*) AS action_count
FROM lifecycle_management
GROUP BY subscriber_name, data_source, lifecycle_action
ORDER BY lifecycle_action, action_count DESC;

Troubleshooting

Common Issues

Subscription Creation Failures

-- Check data source availability
SELECT * FROM sap_odp_show('BW')
WHERE data_source = 'VBAK$F';

-- Verify data source structure
SELECT * FROM sap_odp_describe('BW', 'VBAK$F');

-- Test with preview
SELECT * FROM sap_odp_preview('BW', 'VBAK$F', LIMIT => 10);

Cursor Position Issues

-- Check cursor status
SELECT * FROM sap_odp_show_cursors()
WHERE subscriber_name = 'ERPL';

-- Analyze cursor positions
SELECT
data_source,
replication_mode,
cursor_position,
last_extraction_date,
records_extracted
FROM sap_odp_show_cursors()
ORDER BY last_extraction_date DESC;

Recovery Failures

-- Check subscription error details
SELECT
subscriber_name,
data_source,
error_message,
last_used_date
FROM sap_odp_show_subscriptions()
WHERE status = 'ERROR';

-- Attempt manual recovery
SELECT * FROM sap_odp_read_full(
'BW',
'VBAK$F',
replication_mode => 'RECOVER'
);

Next Steps

🚀 Ready for More?

🔧 Advanced Topics

💡 Examples


Need help? Check our troubleshooting guide or browse more examples.