# Scheduled Syncs Guide

## Overview

Pulse supports automatic scheduled syncs using node-cron. This allows you to configure daily, weekly, or custom sync schedules without manual intervention.

---

## Features

- ✅ **Configurable Schedules** - Create multiple sync schedules with different frequencies
- ✅ **Cron Expressions** - Full cron syntax support for flexible scheduling
- ✅ **Admin UI** - Manage schedules through the web interface
- ✅ **Enable/Disable** - Toggle schedules on/off without deleting them
- ✅ **Manual Trigger** - Run any schedule immediately for testing
- ✅ **Status Monitoring** - View last run, next run, and success/failure status
- ✅ **Error Tracking** - See error messages for failed syncs
- ✅ **Incremental or Full** - Choose sync type per schedule

---

## Default Schedules

Two default schedules are created on first startup (disabled by default):

### **1. Daily Incremental Sync**
- **Schedule:** Every day at 2 AM (`0 2 * * *`)
- **Type:** Incremental (last 24 hours)
- **Purpose:** Keep data up-to-date with daily changes
- **Status:** Disabled (enable via UI)

### **2. Weekly Full Sync**
- **Schedule:** Every Sunday at 3 AM (`0 3 * * 0`)
- **Type:** Full (2 years back)
- **Purpose:** Ensure data integrity with complete sync
- **Status:** Disabled (enable via UI)

---

## Managing Schedules

### **Via Admin UI**

1. **Navigate to Sync Page**
   - Go to Admin → Sync
   - Click on "Schedules" tab

2. **View Schedules**
   - See all configured schedules
   - Check status (enabled/disabled, running, last run, next run)
   - View error messages for failed syncs

3. **Enable/Disable Schedule**
   - Click the Play/Pause button
   - Schedule starts/stops immediately

4. **Manually Trigger Schedule**
   - Click the Clock button
   - Sync runs immediately (doesn't affect schedule)

5. **Edit Schedule**
   - Click "Edit" button
   - Modify name, description, cron expression, sync type
   - Changes take effect immediately

6. **Create New Schedule**
   - Click "New Schedule" button
   - Fill in details:
     - **ID:** Unique identifier (e.g., `hourly-incremental`)
     - **Name:** Display name (e.g., `Hourly Incremental Sync`)
     - **Description:** What this schedule does
     - **Sync Type:** Incremental or Full
     - **Cron Expression:** When to run (use presets or custom)
     - **Enable:** Start immediately or leave disabled

7. **Delete Schedule**
   - Click Trash button
   - Confirm deletion
   - Schedule is permanently removed

### **Via API**

**Get All Schedules:**
```bash
curl http://localhost:3100/api/sync/schedules
```

**Get Specific Schedule:**
```bash
curl http://localhost:3100/api/sync/schedules/daily-incremental
```

**Create Schedule:**
```bash
curl -X POST http://localhost:3100/api/sync/schedules \
  -H "Content-Type: application/json" \
  -d '{
    "id": "hourly-tickets",
    "name": "Hourly Ticket Sync",
    "description": "Sync tickets every hour",
    "cron_expression": "0 * * * *",
    "sync_type": "incremental",
    "is_enabled": true
  }'
```

**Update Schedule:**
```bash
curl -X PATCH http://localhost:3100/api/sync/schedules/daily-incremental \
  -H "Content-Type: application/json" \
  -d '{
    "is_enabled": true,
    "cron_expression": "0 3 * * *"
  }'
```

**Delete Schedule:**
```bash
curl -X DELETE http://localhost:3100/api/sync/schedules/hourly-tickets
```

**Trigger Schedule Manually:**
```bash
curl -X POST http://localhost:3100/api/sync/schedules/daily-incremental/trigger
```

---

## Cron Expression Guide

Cron format: `minute hour day month weekday`

### **Common Patterns**

| Expression | Description |
|------------|-------------|
| `0 2 * * *` | Every day at 2 AM |
| `0 3 * * 0` | Every Sunday at 3 AM |
| `0 */6 * * *` | Every 6 hours |
| `0 0 1 * *` | First day of every month at midnight |
| `30 4 * * 1-5` | 4:30 AM on weekdays |
| `0 */2 * * *` | Every 2 hours |
| `15 14 1 * *` | 2:15 PM on the first of every month |

### **Field Values**

- **Minute:** 0-59
- **Hour:** 0-23 (0 = midnight, 12 = noon)
- **Day:** 1-31
- **Month:** 1-12
- **Weekday:** 0-7 (0 or 7 = Sunday)

### **Special Characters**

- `*` - Any value
- `,` - List (e.g., `1,15` = 1st and 15th)
- `-` - Range (e.g., `1-5` = Monday through Friday)
- `/` - Step (e.g., `*/2` = every 2 units)

### **Examples**

```
0 2 * * *       # 2:00 AM every day
0 3 * * 0       # 3:00 AM every Sunday
0 */4 * * *     # Every 4 hours
30 9 * * 1-5    # 9:30 AM on weekdays
0 0 1,15 * *    # Midnight on 1st and 15th
```

---

## Recommended Schedules

### **Small Organization (< 100 tickets/day)**

```
Daily Incremental:  0 2 * * *   (2 AM daily)
Weekly Full:        0 3 * * 0   (3 AM Sunday)
```

### **Medium Organization (100-500 tickets/day)**

```
Incremental:        0 */6 * * *  (Every 6 hours)
Weekly Full:        0 3 * * 0    (3 AM Sunday)
```

### **Large Organization (> 500 tickets/day)**

```
Incremental:        0 */2 * * *  (Every 2 hours)
Daily Full:         0 3 * * *    (3 AM daily)
```

### **With Webhooks**

If using webhooks for real-time updates:

```
Daily Incremental:  0 2 * * *   (Backup for missed webhooks)
Weekly Full:        0 3 * * 0   (Data integrity check)
```

---

## Best Practices

### **1. Stagger Schedules**

Don't run multiple syncs at the same time:
```
Daily Incremental:  0 2 * * *   (2 AM)
Weekly Full:        0 3 * * 0   (3 AM Sunday)
```

### **2. Off-Peak Hours**

Schedule syncs during low-usage periods:
- ✅ 2-4 AM (recommended)
- ✅ Late evening (10 PM - midnight)
- ❌ Business hours (9 AM - 5 PM)

### **3. Start Disabled**

Create new schedules disabled, test manually first:
```json
{
  "is_enabled": false
}
```

Then enable after verifying it works.

### **4. Monitor First Runs**

After enabling a schedule:
1. Wait for first scheduled run
2. Check sync history for success
3. Review any error messages
4. Adjust schedule if needed

### **5. Incremental + Full Strategy**

Combine both for best results:
- **Incremental:** Daily or more frequent (fast, recent changes)
- **Full:** Weekly or monthly (slow, ensures data integrity)

### **6. Test with Manual Trigger**

Before enabling a schedule:
1. Create schedule (disabled)
2. Click "Trigger" button to run manually
3. Verify sync completes successfully
4. Enable schedule

---

## Monitoring

### **Schedule Status**

Each schedule shows:
- **Enabled/Disabled** - Current state
- **Running** - Currently executing (animated badge)
- **Next Run** - When it will run next (e.g., "in 2h 15m")
- **Last Run** - When it last executed
- **Last Status** - Success ✓ or Failed ✗
- **Error Message** - Details if failed

### **Check Logs**

View application logs for scheduler activity:

```bash
# Docker logs
docker logs pulse-app | grep SCHEDULER

# Recent scheduler events
docker logs pulse-app --tail 100 | grep SCHEDULER
```

### **Database Queries**

```sql
-- View all schedules
SELECT * FROM sync_schedules ORDER BY id;

-- View enabled schedules
SELECT id, name, cron_expression, next_run 
FROM sync_schedules 
WHERE is_enabled = true;

-- View failed schedules
SELECT id, name, last_run, last_error 
FROM sync_schedules 
WHERE last_status = 'failed';

-- View schedule history
SELECT 
  s.name,
  s.last_run,
  s.last_status,
  h.records_added,
  h.records_updated
FROM sync_schedules s
LEFT JOIN sync_history h ON h.started_at = s.last_run
WHERE s.last_run IS NOT NULL
ORDER BY s.last_run DESC;
```

---

## Troubleshooting

### **Schedule Not Running**

**Check 1: Is it enabled?**
- Look for "Enabled" badge
- If disabled, click Play button

**Check 2: Is cron expression valid?**
- Look for "Invalid Cron" badge
- Edit schedule and fix expression

**Check 3: Check next run time**
- Ensure next run is in the future
- If "Calculating...", wait a moment and refresh

**Check 4: Application running?**
- Scheduler only works when app is running
- Check `docker ps` or process status

**Check 5: Check logs**
```bash
docker logs pulse-app | grep "SCHEDULER.*daily-incremental"
```

### **Schedule Failing**

**Check Error Message:**
- View in UI under schedule card
- Shows last error from failed sync

**Common Errors:**

1. **"A sync operation is already in progress"**
   - Another sync is running
   - Wait for it to complete
   - Consider adjusting schedule times

2. **"Failed to connect to Autotask API"**
   - Check API credentials
   - Verify network connectivity
   - Check Autotask API status

3. **"Database connection error"**
   - Check PostgreSQL is running
   - Verify database credentials
   - Check disk space

**View Sync History:**
- Go to "Sync History" tab
- Filter by entity type
- Check error details

### **Schedule Running Too Long**

If a sync takes longer than expected:

1. **Check current sync status:**
   ```bash
   curl http://localhost:3100/api/sync/status
   ```

2. **Review sync history:**
   - Look at duration of previous syncs
   - Identify slow entities

3. **Consider splitting:**
   - Create separate schedules for slow entities
   - Run them at different times

### **Missed Schedules**

If application was down during scheduled time:
- Schedule will NOT run retroactively
- Next run will be at next scheduled time
- Consider manual trigger if data is critical

---

## Database Schema

### **sync_schedules Table**

```sql
CREATE TABLE sync_schedules (
  id VARCHAR(50) PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  description TEXT,
  cron_expression VARCHAR(50) NOT NULL,
  sync_type VARCHAR(20) NOT NULL,  -- 'incremental' or 'full'
  years_back INTEGER DEFAULT 2,
  is_enabled BOOLEAN NOT NULL DEFAULT true,
  last_run TIMESTAMP,
  next_run TIMESTAMP,
  last_status VARCHAR(20),         -- 'success' or 'failed'
  last_error TEXT,
  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
```

---

## Advanced Configuration

### **Environment Variables**

Currently, schedules are managed via database and UI. Future versions may support:

```env
# Example (not yet implemented)
ENABLE_SCHEDULED_SYNC=true
DEFAULT_INCREMENTAL_CRON=0 2 * * *
DEFAULT_FULL_CRON=0 3 * * 0
```

### **Custom Schedules**

Create specialized schedules for specific needs:

**High-Priority Entities:**
```json
{
  "id": "hourly-tickets",
  "name": "Hourly Ticket Sync",
  "cron_expression": "0 * * * *",
  "sync_type": "incremental"
}
```

**Monthly Reports:**
```json
{
  "id": "monthly-full",
  "name": "Monthly Full Sync",
  "cron_expression": "0 4 1 * *",
  "sync_type": "full",
  "years_back": 5
}
```

**Business Hours Only:**
```json
{
  "id": "business-hours",
  "name": "Business Hours Sync",
  "cron_expression": "0 9-17 * * 1-5",
  "sync_type": "incremental"
}
```

---

## Performance Considerations

### **Concurrent Syncs**

- Scheduler prevents concurrent runs of the SAME schedule
- Different schedules CAN run concurrently
- Sync service prevents multiple syncs system-wide

### **Resource Usage**

**During Sync:**
- CPU: Moderate (data processing)
- Memory: Moderate (batch processing)
- Network: High (API calls)
- Database: Moderate (bulk inserts)

**Recommendations:**
- Schedule during off-peak hours
- Monitor server resources
- Adjust frequency based on load

### **API Rate Limits**

Autotask has rate limits:
- Be mindful of sync frequency
- Incremental syncs use fewer API calls
- Full syncs can hit rate limits on large datasets

---

## Migration from Manual Syncs

### **Step 1: Document Current Process**
- How often do you sync manually?
- Which entities do you sync?
- What time of day?

### **Step 2: Create Equivalent Schedules**
- Daily manual sync → Daily incremental schedule
- Weekly manual sync → Weekly full schedule

### **Step 3: Test Schedules**
- Create schedules (disabled)
- Trigger manually to test
- Verify results in sync history

### **Step 4: Enable Gradually**
- Enable one schedule at a time
- Monitor for 1 week
- Adjust as needed

### **Step 5: Stop Manual Syncs**
- Once confident in automated syncs
- Keep manual option for emergencies

---

## Security

### **Access Control**

Currently, schedule management requires:
- Access to admin interface
- No authentication implemented yet

**Future considerations:**
- Role-based access control
- Audit logging for schedule changes
- API key authentication

### **Schedule Validation**

- Cron expressions validated before saving
- Invalid expressions rejected
- Prevents malicious or broken schedules

---

## Backup and Recovery

### **Backup Schedules**

Export schedule configuration:

```sql
-- Export schedules
COPY (
  SELECT id, name, description, cron_expression, sync_type, years_back, is_enabled
  FROM sync_schedules
) TO '/tmp/schedules_backup.csv' CSV HEADER;
```

### **Restore Schedules**

Import schedule configuration:

```sql
-- Import schedules
COPY sync_schedules (id, name, description, cron_expression, sync_type, years_back, is_enabled)
FROM '/tmp/schedules_backup.csv' CSV HEADER;
```

Or use API to recreate schedules.

---

## FAQ

**Q: Can I have multiple schedules running at once?**
A: Different schedules can run concurrently, but the sync service prevents multiple syncs system-wide. If one schedule is running, others will wait.

**Q: What happens if the app restarts during a scheduled sync?**
A: The sync will be interrupted. The schedule will run again at the next scheduled time.

**Q: Can I change a schedule while it's running?**
A: Yes, but changes won't affect the current run. They'll apply to the next scheduled run.

**Q: Do schedules run if the app is stopped?**
A: No. Schedules only run when the application is running. Consider using systemd or Docker restart policies.

**Q: Can I schedule specific entities?**
A: Not yet. Schedules sync all entities. This may be added in a future version.

**Q: What timezone are schedules in?**
A: Schedules use the server's timezone. Check with `date` command on the server.

**Q: Can I get notifications when syncs fail?**
A: Not yet. Check the UI or logs. Notifications may be added in a future version.

---

## Summary

**Scheduled syncs provide:**
- ✅ Automatic data synchronization
- ✅ Flexible scheduling with cron expressions
- ✅ Easy management via admin UI
- ✅ Status monitoring and error tracking
- ✅ Manual trigger for testing
- ✅ Multiple schedules for different needs

**Recommended setup:**
1. Enable daily incremental sync (2 AM)
2. Enable weekly full sync (Sunday 3 AM)
3. Monitor for first week
4. Adjust as needed based on your usage

**Best combined with:**
- Webhooks for real-time updates
- Manual syncs for immediate needs
- Regular monitoring of sync history
