Add influxdb export/import + add new influxdb instance for prod
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -380,3 +380,6 @@ src/Managing.Infrastructure.Tests/PrivateKeys.cs
|
||||
# Node.js Tools for Visual Studio
|
||||
node_modules/
|
||||
|
||||
# InfluxDB exports and backups
|
||||
scripts/influxdb/exports/
|
||||
|
||||
|
||||
345
scripts/influxdb/README.md
Normal file
345
scripts/influxdb/README.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# InfluxDB Export and Import Scripts
|
||||
|
||||
This directory contains scripts for exporting and importing InfluxDB data for the Managing Apps project using query-based methods that work with standard read/write tokens.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **InfluxDB CLI** - Required for export/import operations
|
||||
```bash
|
||||
brew install influxdb-cli
|
||||
```
|
||||
|
||||
2. **jq** - JSON parser for reading configuration files
|
||||
```bash
|
||||
brew install jq
|
||||
```
|
||||
|
||||
## Available Scripts
|
||||
|
||||
### 1. `export-prices-bucket.sh`
|
||||
Exports OHLCV candle/price data from the `prices-bucket`.
|
||||
|
||||
**What it exports:**
|
||||
- All candle data (open, high, low, close, volume)
|
||||
- Multiple exchanges, tickers, and timeframes
|
||||
- Configurable time ranges
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./export-prices-bucket.sh
|
||||
```
|
||||
|
||||
**Interactive Prompts:**
|
||||
- Select environment (SandboxLocal or ProductionLocal)
|
||||
- Select time range (7 days, 30 days, 90 days, 1 year, all data, or custom)
|
||||
|
||||
**Output:**
|
||||
- CSV export: `./exports/<ENVIRONMENT>/<TIMESTAMP>/prices-bucket_data.csv`
|
||||
- Metadata file with export details
|
||||
|
||||
**Advantages:**
|
||||
- ✅ Works with regular read tokens (no admin required)
|
||||
- ✅ Flexible time range selection
|
||||
- ✅ Exports in standard CSV format
|
||||
- ✅ Can be imported to any InfluxDB instance
|
||||
|
||||
---
|
||||
|
||||
### 2. `import-csv-data.sh`
|
||||
Imports prices-bucket CSV export data into any InfluxDB environment.
|
||||
|
||||
**What it imports:**
|
||||
- Prices-bucket data only
|
||||
- Supports large files (1.6M+ data points)
|
||||
- Automatically creates bucket if needed
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./import-csv-data.sh
|
||||
```
|
||||
|
||||
**Interactive Prompts:**
|
||||
1. Select source environment (which export to import from)
|
||||
2. Select export timestamp
|
||||
3. Select target environment (where to import to)
|
||||
4. Confirm the import operation
|
||||
|
||||
**Features:**
|
||||
- ✅ Imports CSV exports to any environment
|
||||
- ✅ Works with regular read/write tokens
|
||||
- ✅ Batch processing for large files (5000 points per batch)
|
||||
- ✅ Automatic bucket creation if needed
|
||||
- ✅ Progress tracking for large imports
|
||||
|
||||
**⚠️ Note:** Import adds data to the bucket. Existing data with the same timestamps will be overwritten.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
The scripts automatically read InfluxDB connection settings from:
|
||||
- `src/Managing.Api/appsettings.SandboxLocal.json`
|
||||
- `src/Managing.Api/appsettings.ProductionLocal.json`
|
||||
|
||||
**Required settings in appsettings files:**
|
||||
```json
|
||||
{
|
||||
"InfluxDb": {
|
||||
"Url": "https://influx-db.apps.managing.live",
|
||||
"Organization": "managing-org",
|
||||
"Token": "your-token-here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Export/Import Structure
|
||||
|
||||
```
|
||||
exports/
|
||||
├── SandboxLocal/
|
||||
│ └── 20241028_143022/
|
||||
│ ├── prices-bucket_data.csv
|
||||
│ └── export-metadata.txt
|
||||
└── ProductionLocal/
|
||||
└── 20241028_160000/
|
||||
├── prices-bucket_data.csv
|
||||
└── export-metadata.txt
|
||||
```
|
||||
|
||||
## Data Structure
|
||||
|
||||
### prices-bucket (Managed by these scripts)
|
||||
- **Measurement**: `price`
|
||||
- **Contains**: OHLCV candle data
|
||||
- **Tags**:
|
||||
- `exchange` (e.g., Evm, Binance)
|
||||
- `ticker` (e.g., BTC, ETH, AAVE)
|
||||
- `timeframe` (e.g., FifteenMinutes, OneHour, OneDay)
|
||||
- **Fields**:
|
||||
- `open`, `high`, `low`, `close` (price values)
|
||||
- `baseVolume`, `quoteVolume` (volume data)
|
||||
- `TradeCount` (number of trades)
|
||||
- `takerBuyBaseVolume`, `takerBuyQuoteVolume` (taker buy volumes)
|
||||
|
||||
### agent-balances-bucket (Not included in export/import scripts)
|
||||
- **Measurement**: `agent_balance`
|
||||
- **Contains**: User balance history over time
|
||||
- **Note**: This bucket is not managed by these scripts. Balance data is derived from operational data and should be regenerated rather than migrated.
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Quick Export
|
||||
```bash
|
||||
cd scripts/influxdb
|
||||
./export-prices-bucket.sh
|
||||
# Select: 1 (SandboxLocal)
|
||||
# Select: 5 (All data)
|
||||
```
|
||||
|
||||
### Export Specific Time Range
|
||||
```bash
|
||||
cd scripts/influxdb
|
||||
./export-prices-bucket.sh
|
||||
# Select: 1 (SandboxLocal)
|
||||
# Select: 3 (Last 90 days)
|
||||
```
|
||||
|
||||
### Migrate Sandbox to Production
|
||||
```bash
|
||||
cd scripts/influxdb
|
||||
|
||||
# Step 1: Export from sandbox
|
||||
./export-prices-bucket.sh
|
||||
# Select: 1 (SandboxLocal)
|
||||
# Select: 5 (All data)
|
||||
|
||||
# Step 2: Import to production
|
||||
./import-csv-data.sh
|
||||
# Select source: 1 (SandboxLocal)
|
||||
# Select: Latest export timestamp
|
||||
# Select target: 2 (ProductionLocal)
|
||||
# Confirm: yes
|
||||
```
|
||||
|
||||
### Backup Before Major Changes
|
||||
```bash
|
||||
cd scripts/influxdb
|
||||
|
||||
# Export current production data
|
||||
./export-prices-bucket.sh
|
||||
# Select: 2 (ProductionLocal)
|
||||
# Select: 5 (All data)
|
||||
|
||||
# If something goes wrong, restore it:
|
||||
./import-csv-data.sh
|
||||
# Select the backup you just created
|
||||
```
|
||||
|
||||
### Clone Environment
|
||||
```bash
|
||||
# Export from source
|
||||
./export-prices-bucket.sh
|
||||
# Select source environment
|
||||
|
||||
# Import to target
|
||||
./import-csv-data.sh
|
||||
# Select target environment
|
||||
```
|
||||
|
||||
## Token Permissions
|
||||
|
||||
### Read Token (Export)
|
||||
Required for `export-prices-bucket.sh`:
|
||||
- ✅ Read access to buckets
|
||||
- ✅ This is what you typically have in production
|
||||
|
||||
### Write Token (Import)
|
||||
Required for `import-csv-data.sh`:
|
||||
- ✅ Read/Write access to target bucket
|
||||
- ✅ Ability to create buckets (optional, for auto-creation)
|
||||
|
||||
### How to Check Your Token Permissions
|
||||
```bash
|
||||
influx auth list --host <URL> --token <TOKEN>
|
||||
```
|
||||
|
||||
## Data Retention
|
||||
|
||||
- Exports are stored indefinitely by default
|
||||
- Manual cleanup recommended:
|
||||
```bash
|
||||
# Remove exports older than 90 days
|
||||
find ./exports -type d -mtime +90 -exec rm -rf {} +
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "influx command not found"
|
||||
Install InfluxDB CLI:
|
||||
```bash
|
||||
brew install influxdb-cli
|
||||
```
|
||||
|
||||
### "jq command not found"
|
||||
Install jq:
|
||||
```bash
|
||||
brew install jq
|
||||
```
|
||||
|
||||
### "Failed to parse configuration"
|
||||
Ensure the appsettings JSON file exists and is valid JSON.
|
||||
|
||||
### "Connection refused"
|
||||
- Check that InfluxDB URL is accessible
|
||||
- Verify network connectivity to the server
|
||||
- Check firewall rules
|
||||
|
||||
### "401 Unauthorized"
|
||||
- Verify the InfluxDB token in appsettings is correct
|
||||
- For exports: ensure token has read permissions for the bucket
|
||||
- For imports: ensure token has write permissions for the bucket
|
||||
|
||||
### "Bucket not found"
|
||||
The import script will automatically create the bucket if you have permissions.
|
||||
|
||||
Or create it manually:
|
||||
```bash
|
||||
influx bucket create \
|
||||
--name prices-bucket \
|
||||
--org managing-org \
|
||||
--retention 0 \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--token YOUR_TOKEN
|
||||
```
|
||||
|
||||
### Import is slow
|
||||
- This is normal for large files (240MB+ with 1.6M+ data points)
|
||||
- Expected time: 5-15 minutes depending on network speed
|
||||
- The script processes data in batches of 5000 points
|
||||
- Progress is shown during import
|
||||
|
||||
### Duplicate data after import
|
||||
- Imports overwrite data with the same timestamp
|
||||
- To avoid duplicates, don't import the same data twice
|
||||
- To replace all data: delete the bucket first, then import
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### For Large Exports
|
||||
- Export specific time ranges instead of all data when possible
|
||||
- Exports are faster than full database dumps
|
||||
- CSV files compress well (use `gzip` for storage)
|
||||
|
||||
### For Large Imports
|
||||
- Import during low-traffic periods
|
||||
- Monitor InfluxDB memory usage during import
|
||||
- Consider splitting very large imports into time ranges
|
||||
|
||||
## Verify Data After Import
|
||||
|
||||
```bash
|
||||
# Check recent data
|
||||
influx query 'from(bucket:"prices-bucket") |> range(start:-7d) |> limit(n:10)' \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--org managing-org \
|
||||
--token YOUR_TOKEN
|
||||
|
||||
# Count total records
|
||||
influx query 'from(bucket:"prices-bucket") |> range(start:2020-01-01T00:00:00Z) |> count()' \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--org managing-org \
|
||||
--token YOUR_TOKEN
|
||||
|
||||
# Check specific ticker
|
||||
influx query 'from(bucket:"prices-bucket") |> range(start:-30d) |> filter(fn: (r) => r.ticker == "BTC")' \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--org managing-org \
|
||||
--token YOUR_TOKEN
|
||||
```
|
||||
|
||||
## Manual Export/Import Commands
|
||||
|
||||
If you need to run commands manually:
|
||||
|
||||
**Export:**
|
||||
```bash
|
||||
influx query 'from(bucket: "prices-bucket") |> range(start: -30d)' \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--org managing-org \
|
||||
--token YOUR_TOKEN \
|
||||
--raw > export.csv
|
||||
```
|
||||
|
||||
**Import:**
|
||||
```bash
|
||||
influx write \
|
||||
--host https://influx-db.apps.managing.live \
|
||||
--org managing-org \
|
||||
--token YOUR_TOKEN \
|
||||
--bucket prices-bucket \
|
||||
--format csv \
|
||||
--file export.csv
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Exports**: Schedule regular exports of production data
|
||||
2. **Test Imports**: Test imports on sandbox before production
|
||||
3. **Verify After Import**: Always verify data integrity after import
|
||||
4. **Document Changes**: Keep notes of what data was imported when
|
||||
5. **Backup Before Major Changes**: Export before major data operations
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions, refer to:
|
||||
- [InfluxDB Query Documentation](https://docs.influxdata.com/influxdb/v2.0/query-data/)
|
||||
- [InfluxDB Write Documentation](https://docs.influxdata.com/influxdb/v2.0/write-data/)
|
||||
- Project documentation in `/docs`
|
||||
|
||||
## Script Locations
|
||||
|
||||
All scripts are located in: `/Users/oda/Desktop/Projects/managing-apps/scripts/influxdb/`
|
||||
|
||||
Configuration files:
|
||||
- Sandbox: `/Users/oda/Desktop/Projects/managing-apps/src/Managing.Api/appsettings.SandboxLocal.json`
|
||||
- Production: `/Users/oda/Desktop/Projects/managing-apps/src/Managing.Api/appsettings.ProductionLocal.json`
|
||||
265
scripts/influxdb/export-prices-bucket.sh
Executable file
265
scripts/influxdb/export-prices-bucket.sh
Executable file
@@ -0,0 +1,265 @@
|
||||
#!/bin/bash
|
||||
|
||||
# InfluxDB Prices Bucket Data Export Script (No Admin Required)
|
||||
# Usage: ./export-prices-bucket.sh
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Get the directory where the script is located
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
SRC_DIR="$PROJECT_ROOT/src"
|
||||
|
||||
# Logging functions
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}"
|
||||
}
|
||||
|
||||
# Check if influx CLI is installed
|
||||
command -v influx >/dev/null 2>&1 || error "InfluxDB CLI is not installed. Please install it first: brew install influxdb-cli"
|
||||
|
||||
# Check if jq is installed for JSON parsing
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
warn "jq is not installed. Installing it for JSON parsing..."
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
brew install jq || error "Failed to install jq. Please install it manually: brew install jq"
|
||||
else
|
||||
error "jq is not installed and brew is not available. Please install jq manually."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prompt for environment
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo " InfluxDB Prices Data Export"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Select environment:"
|
||||
echo "1) SandboxLocal"
|
||||
echo "2) ProductionLocal"
|
||||
echo ""
|
||||
read -p "Enter your choice (1 or 2): " ENV_CHOICE
|
||||
|
||||
case $ENV_CHOICE in
|
||||
1)
|
||||
ENVIRONMENT="SandboxLocal"
|
||||
APPSETTINGS_FILE="$SRC_DIR/Managing.Api/appsettings.SandboxLocal.json"
|
||||
;;
|
||||
2)
|
||||
ENVIRONMENT="ProductionLocal"
|
||||
APPSETTINGS_FILE="$SRC_DIR/Managing.Api/appsettings.ProductionLocal.json"
|
||||
;;
|
||||
*)
|
||||
error "Invalid choice. Please run the script again and select 1 or 2."
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Selected environment: $ENVIRONMENT"
|
||||
|
||||
# Check if appsettings file exists
|
||||
if [ ! -f "$APPSETTINGS_FILE" ]; then
|
||||
error "Configuration file not found: $APPSETTINGS_FILE"
|
||||
fi
|
||||
|
||||
log "Reading configuration from: $APPSETTINGS_FILE"
|
||||
|
||||
# Parse InfluxDB settings from JSON
|
||||
INFLUX_URL=$(jq -r '.InfluxDb.Url' "$APPSETTINGS_FILE")
|
||||
INFLUX_ORG=$(jq -r '.InfluxDb.Organization' "$APPSETTINGS_FILE")
|
||||
INFLUX_TOKEN=$(jq -r '.InfluxDb.Token' "$APPSETTINGS_FILE")
|
||||
|
||||
# Validate parsed values
|
||||
if [ "$INFLUX_URL" = "null" ] || [ -z "$INFLUX_URL" ]; then
|
||||
error "Failed to parse InfluxDb.Url from configuration file"
|
||||
fi
|
||||
|
||||
if [ "$INFLUX_ORG" = "null" ] || [ -z "$INFLUX_ORG" ]; then
|
||||
error "Failed to parse InfluxDb.Organization from configuration file"
|
||||
fi
|
||||
|
||||
if [ "$INFLUX_TOKEN" = "null" ] || [ -z "$INFLUX_TOKEN" ]; then
|
||||
error "Failed to parse InfluxDb.Token from configuration file"
|
||||
fi
|
||||
|
||||
info "InfluxDB URL: $INFLUX_URL"
|
||||
info "Organization: $INFLUX_ORG"
|
||||
info "Token: ${INFLUX_TOKEN:0:20}..." # Only show first 20 chars for security
|
||||
|
||||
# Prompt for time range
|
||||
echo ""
|
||||
info "Select time range for export:"
|
||||
echo "1) Last 7 days"
|
||||
echo "2) Last 30 days"
|
||||
echo "3) Last 90 days"
|
||||
echo "4) Last 1 year"
|
||||
echo "5) All data (from 2020-01-01)"
|
||||
echo "6) Custom range"
|
||||
echo ""
|
||||
read -p "Enter your choice (1-6): " TIME_CHOICE
|
||||
|
||||
case $TIME_CHOICE in
|
||||
1)
|
||||
START_TIME="-7d"
|
||||
TIME_DESC="Last 7 days"
|
||||
;;
|
||||
2)
|
||||
START_TIME="-30d"
|
||||
TIME_DESC="Last 30 days"
|
||||
;;
|
||||
3)
|
||||
START_TIME="-90d"
|
||||
TIME_DESC="Last 90 days"
|
||||
;;
|
||||
4)
|
||||
START_TIME="-1y"
|
||||
TIME_DESC="Last 1 year"
|
||||
;;
|
||||
5)
|
||||
START_TIME="2020-01-01T00:00:00Z"
|
||||
TIME_DESC="All data"
|
||||
;;
|
||||
6)
|
||||
read -p "Enter start date (YYYY-MM-DD): " START_DATE
|
||||
START_TIME="${START_DATE}T00:00:00Z"
|
||||
TIME_DESC="From $START_DATE"
|
||||
;;
|
||||
*)
|
||||
error "Invalid choice"
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Time range: $TIME_DESC"
|
||||
|
||||
# Create export directory with timestamp
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
EXPORT_BASE_DIR="$SCRIPT_DIR/exports"
|
||||
EXPORT_DIR="$EXPORT_BASE_DIR/$ENVIRONMENT/$TIMESTAMP"
|
||||
|
||||
log "Creating export directory: $EXPORT_DIR"
|
||||
mkdir -p "$EXPORT_DIR" || error "Failed to create export directory"
|
||||
|
||||
# Bucket name
|
||||
BUCKET_NAME="prices-bucket"
|
||||
|
||||
log "Starting export of '$BUCKET_NAME' bucket..."
|
||||
info "This may take a while depending on the data size..."
|
||||
echo ""
|
||||
|
||||
# Export data using CSV format (more reliable than backup for non-admin tokens)
|
||||
EXPORT_FILE="$EXPORT_DIR/${BUCKET_NAME}_data.csv"
|
||||
|
||||
info "Exporting data to CSV..."
|
||||
|
||||
# Build the Flux query
|
||||
FLUX_QUERY="from(bucket: \"$BUCKET_NAME\")
|
||||
|> range(start: $START_TIME)
|
||||
|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"
|
||||
|
||||
# Export to CSV
|
||||
if influx query "$FLUX_QUERY" \
|
||||
--host "$INFLUX_URL" \
|
||||
--org "$INFLUX_ORG" \
|
||||
--token "$INFLUX_TOKEN" \
|
||||
--raw > "$EXPORT_FILE" 2>&1; then
|
||||
|
||||
log "✅ Export completed successfully!"
|
||||
|
||||
# Get export size
|
||||
EXPORT_SIZE=$(du -sh "$EXPORT_FILE" | cut -f1)
|
||||
info "Export location: $EXPORT_FILE"
|
||||
info "Export size: $EXPORT_SIZE"
|
||||
|
||||
# Count lines (data points)
|
||||
LINE_COUNT=$(wc -l < "$EXPORT_FILE" | xargs)
|
||||
DATA_POINTS=$((LINE_COUNT - 1)) # Subtract header
|
||||
info "Data points exported: $DATA_POINTS"
|
||||
|
||||
# Save export metadata
|
||||
METADATA_FILE="$EXPORT_DIR/export-metadata.txt"
|
||||
cat > "$METADATA_FILE" << EOF
|
||||
InfluxDB Export Metadata
|
||||
========================
|
||||
Date: $(date)
|
||||
Environment: $ENVIRONMENT
|
||||
Bucket: $BUCKET_NAME
|
||||
Time Range: $TIME_DESC
|
||||
Start Time: $START_TIME
|
||||
InfluxDB URL: $INFLUX_URL
|
||||
Organization: $INFLUX_ORG
|
||||
Export File: $EXPORT_FILE
|
||||
Export Size: $EXPORT_SIZE
|
||||
Data Points: $DATA_POINTS
|
||||
Configuration File: $APPSETTINGS_FILE
|
||||
|
||||
Flux Query Used:
|
||||
----------------
|
||||
$FLUX_QUERY
|
||||
EOF
|
||||
|
||||
log "Metadata saved to: $METADATA_FILE"
|
||||
|
||||
# Also save as line protocol for easier restore
|
||||
info "Converting to line protocol format..."
|
||||
LP_FILE="$EXPORT_DIR/${BUCKET_NAME}_data.lp"
|
||||
|
||||
# Use influx query with --raw format for line protocol
|
||||
FLUX_QUERY_LP="from(bucket: \"$BUCKET_NAME\")
|
||||
|> range(start: $START_TIME)"
|
||||
|
||||
if influx query "$FLUX_QUERY_LP" \
|
||||
--host "$INFLUX_URL" \
|
||||
--org "$INFLUX_ORG" \
|
||||
--token "$INFLUX_TOKEN" \
|
||||
--raw > "$LP_FILE.tmp" 2>&1; then
|
||||
|
||||
# Clean up the output (remove annotations)
|
||||
grep -v "^#" "$LP_FILE.tmp" > "$LP_FILE" 2>/dev/null || true
|
||||
rm -f "$LP_FILE.tmp"
|
||||
|
||||
LP_SIZE=$(du -sh "$LP_FILE" | cut -f1 2>/dev/null || echo "0")
|
||||
if [ -s "$LP_FILE" ]; then
|
||||
info "Line protocol export: $LP_FILE ($LP_SIZE)"
|
||||
else
|
||||
warn "Line protocol export is empty, using CSV only"
|
||||
rm -f "$LP_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log "🎉 Export process completed successfully!"
|
||||
echo ""
|
||||
info "Export files:"
|
||||
ls -lh "$EXPORT_DIR"
|
||||
echo ""
|
||||
info "To restore this data, you can use:"
|
||||
echo " 1. CSV import via InfluxDB UI"
|
||||
echo " 2. Or use the import-prices-data.sh script (coming soon)"
|
||||
if [ -f "$LP_FILE" ]; then
|
||||
echo " 3. Line protocol: influx write --bucket $BUCKET_NAME --file \"$LP_FILE\""
|
||||
fi
|
||||
echo ""
|
||||
|
||||
else
|
||||
error "Export failed! Check the error messages above."
|
||||
fi
|
||||
|
||||
378
scripts/influxdb/import-csv-data.sh
Executable file
378
scripts/influxdb/import-csv-data.sh
Executable file
@@ -0,0 +1,378 @@
|
||||
#!/bin/bash
|
||||
|
||||
# InfluxDB CSV Data Import Script
|
||||
# Usage: ./import-csv-data.sh
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Get the directory where the script is located
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
SRC_DIR="$PROJECT_ROOT/src"
|
||||
EXPORTS_BASE_DIR="$SCRIPT_DIR/exports"
|
||||
|
||||
# Logging functions
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}"
|
||||
}
|
||||
|
||||
# Check if influx CLI is installed
|
||||
command -v influx >/dev/null 2>&1 || error "InfluxDB CLI is not installed. Please install it first: brew install influxdb-cli"
|
||||
|
||||
# Check if jq is installed for JSON parsing
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
warn "jq is not installed. Installing it for JSON parsing..."
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
brew install jq || error "Failed to install jq. Please install it manually: brew install jq"
|
||||
else
|
||||
error "jq is not installed and brew is not available. Please install jq manually."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " InfluxDB CSV Data Import"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# Check if exports directory exists
|
||||
if [ ! -d "$EXPORTS_BASE_DIR" ]; then
|
||||
error "Exports directory not found: $EXPORTS_BASE_DIR"
|
||||
fi
|
||||
|
||||
# List available source environments
|
||||
echo "Available export source environments:"
|
||||
ENVIRONMENTS=($(ls -d "$EXPORTS_BASE_DIR"/*/ 2>/dev/null | xargs -n 1 basename))
|
||||
|
||||
if [ ${#ENVIRONMENTS[@]} -eq 0 ]; then
|
||||
error "No export environments found in: $EXPORTS_BASE_DIR"
|
||||
fi
|
||||
|
||||
for i in "${!ENVIRONMENTS[@]}"; do
|
||||
echo "$((i+1))) ${ENVIRONMENTS[$i]}"
|
||||
done
|
||||
echo ""
|
||||
read -p "Select source environment (1-${#ENVIRONMENTS[@]}): " ENV_CHOICE
|
||||
|
||||
if [ "$ENV_CHOICE" -lt 1 ] || [ "$ENV_CHOICE" -gt ${#ENVIRONMENTS[@]} ]; then
|
||||
error "Invalid choice"
|
||||
fi
|
||||
|
||||
SOURCE_ENV="${ENVIRONMENTS[$((ENV_CHOICE-1))]}"
|
||||
ENV_EXPORT_DIR="$EXPORTS_BASE_DIR/$SOURCE_ENV"
|
||||
|
||||
log "Selected source environment: $SOURCE_ENV"
|
||||
|
||||
# List available export timestamps
|
||||
echo ""
|
||||
echo "Available exports for $SOURCE_ENV:"
|
||||
EXPORTS=($(ls -d "$ENV_EXPORT_DIR"/*/ 2>/dev/null | xargs -n 1 basename | sort -r))
|
||||
|
||||
if [ ${#EXPORTS[@]} -eq 0 ]; then
|
||||
error "No exports found for environment: $SOURCE_ENV"
|
||||
fi
|
||||
|
||||
for i in "${!EXPORTS[@]}"; do
|
||||
EXPORT_PATH="$ENV_EXPORT_DIR/${EXPORTS[$i]}"
|
||||
METADATA_FILE="$EXPORT_PATH/export-metadata.txt"
|
||||
|
||||
if [ -f "$METADATA_FILE" ]; then
|
||||
EXPORT_SIZE=$(grep "Export Size:" "$METADATA_FILE" | cut -d: -f2 | xargs)
|
||||
DATA_POINTS=$(grep "Data Points:" "$METADATA_FILE" | cut -d: -f2 | xargs)
|
||||
EXPORT_DATE=$(grep "Date:" "$METADATA_FILE" | cut -d: -f2- | xargs)
|
||||
echo "$((i+1))) ${EXPORTS[$i]} - $EXPORT_DATE ($EXPORT_SIZE, $DATA_POINTS points)"
|
||||
else
|
||||
echo "$((i+1))) ${EXPORTS[$i]}"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
read -p "Select export to import (1-${#EXPORTS[@]}): " EXPORT_CHOICE
|
||||
|
||||
if [ "$EXPORT_CHOICE" -lt 1 ] || [ "$EXPORT_CHOICE" -gt ${#EXPORTS[@]} ]; then
|
||||
error "Invalid choice"
|
||||
fi
|
||||
|
||||
SELECTED_EXPORT="${EXPORTS[$((EXPORT_CHOICE-1))]}"
|
||||
IMPORT_FROM_DIR="$ENV_EXPORT_DIR/$SELECTED_EXPORT"
|
||||
|
||||
log "Selected export: $SELECTED_EXPORT"
|
||||
info "Export location: $IMPORT_FROM_DIR"
|
||||
|
||||
# Find CSV file
|
||||
CSV_FILE=$(find "$IMPORT_FROM_DIR" -name "*.csv" | head -1)
|
||||
|
||||
if [ ! -f "$CSV_FILE" ]; then
|
||||
error "No CSV file found in: $IMPORT_FROM_DIR"
|
||||
fi
|
||||
|
||||
CSV_SIZE=$(du -sh "$CSV_FILE" | cut -f1)
|
||||
info "CSV file: $(basename "$CSV_FILE") ($CSV_SIZE)"
|
||||
|
||||
# Select target environment for import
|
||||
echo ""
|
||||
echo "Select TARGET environment for import:"
|
||||
echo "1) SandboxLocal"
|
||||
echo "2) ProductionLocal"
|
||||
echo ""
|
||||
read -p "Enter your choice (1 or 2): " TARGET_ENV_CHOICE
|
||||
|
||||
case $TARGET_ENV_CHOICE in
|
||||
1)
|
||||
TARGET_ENVIRONMENT="SandboxLocal"
|
||||
APPSETTINGS_FILE="$SRC_DIR/Managing.Api/appsettings.SandboxLocal.json"
|
||||
;;
|
||||
2)
|
||||
TARGET_ENVIRONMENT="ProductionLocal"
|
||||
APPSETTINGS_FILE="$SRC_DIR/Managing.Api/appsettings.ProductionLocal.json"
|
||||
;;
|
||||
*)
|
||||
error "Invalid choice. Please run the script again and select 1 or 2."
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Target environment: $TARGET_ENVIRONMENT"
|
||||
|
||||
# Check if appsettings file exists
|
||||
if [ ! -f "$APPSETTINGS_FILE" ]; then
|
||||
error "Configuration file not found: $APPSETTINGS_FILE"
|
||||
fi
|
||||
|
||||
log "Reading configuration from: $APPSETTINGS_FILE"
|
||||
|
||||
# Parse InfluxDB settings from JSON
|
||||
INFLUX_URL=$(jq -r '.InfluxDb.Url' "$APPSETTINGS_FILE")
|
||||
INFLUX_ORG=$(jq -r '.InfluxDb.Organization' "$APPSETTINGS_FILE")
|
||||
INFLUX_TOKEN=$(jq -r '.InfluxDb.Token' "$APPSETTINGS_FILE")
|
||||
|
||||
# Validate parsed values
|
||||
if [ "$INFLUX_URL" = "null" ] || [ -z "$INFLUX_URL" ]; then
|
||||
error "Failed to parse InfluxDb.Url from configuration file"
|
||||
fi
|
||||
|
||||
if [ "$INFLUX_ORG" = "null" ] || [ -z "$INFLUX_ORG" ]; then
|
||||
error "Failed to parse InfluxDb.Organization from configuration file"
|
||||
fi
|
||||
|
||||
if [ "$INFLUX_TOKEN" = "null" ] || [ -z "$INFLUX_TOKEN" ]; then
|
||||
error "Failed to parse InfluxDb.Token from configuration file"
|
||||
fi
|
||||
|
||||
info "Target InfluxDB URL: $INFLUX_URL"
|
||||
info "Organization: $INFLUX_ORG"
|
||||
|
||||
# Get bucket name
|
||||
BUCKET_NAME="prices-bucket"
|
||||
|
||||
# Check if bucket exists
|
||||
info "Checking if bucket '$BUCKET_NAME' exists..."
|
||||
if influx bucket list --host "$INFLUX_URL" --org "$INFLUX_ORG" --token "$INFLUX_TOKEN" --name "$BUCKET_NAME" &>/dev/null; then
|
||||
log "✅ Bucket '$BUCKET_NAME' exists"
|
||||
else
|
||||
warn "Bucket '$BUCKET_NAME' does not exist!"
|
||||
read -p "Create the bucket now? (yes/no): " CREATE_BUCKET
|
||||
if [ "$CREATE_BUCKET" = "yes" ]; then
|
||||
influx bucket create \
|
||||
--name "$BUCKET_NAME" \
|
||||
--retention 0 \
|
||||
--host "$INFLUX_URL" \
|
||||
--org "$INFLUX_ORG" \
|
||||
--token "$INFLUX_TOKEN" || error "Failed to create bucket"
|
||||
log "✅ Bucket created successfully"
|
||||
else
|
||||
error "Cannot proceed without target bucket"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final confirmation
|
||||
echo ""
|
||||
warn "⚠️ IMPORTANT INFORMATION:"
|
||||
echo " Source: $SOURCE_ENV/$SELECTED_EXPORT"
|
||||
echo " Target: $TARGET_ENVIRONMENT ($INFLUX_URL)"
|
||||
echo " Bucket: $BUCKET_NAME"
|
||||
echo " Data Size: $CSV_SIZE"
|
||||
warn " This will ADD data to the bucket (existing data will be preserved)"
|
||||
warn " Duplicate timestamps may cause overwrites"
|
||||
echo ""
|
||||
read -p "Are you sure you want to continue? (yes/no): " CONFIRM
|
||||
|
||||
if [ "$CONFIRM" != "yes" ]; then
|
||||
log "Import cancelled by user"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Perform import
|
||||
echo ""
|
||||
log "🚀 Starting import operation..."
|
||||
log "This may take several minutes for large files..."
|
||||
echo ""
|
||||
|
||||
# Create a temporary file for line protocol conversion
|
||||
TEMP_LP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_LP_FILE" EXIT
|
||||
|
||||
info "Converting CSV to line protocol format..."
|
||||
|
||||
# Convert annotated CSV to line protocol using awk
|
||||
# Skip annotation lines (starting with #) and empty lines
|
||||
awk -F',' '
|
||||
BEGIN {OFS=","}
|
||||
# Skip annotation lines
|
||||
/^#/ {next}
|
||||
# Skip empty lines
|
||||
/^[[:space:]]*$/ {next}
|
||||
# Process header to get field positions
|
||||
NR==1 {
|
||||
for (i=1; i<=NF; i++) {
|
||||
field[$i] = i
|
||||
}
|
||||
next
|
||||
}
|
||||
# Process data rows
|
||||
{
|
||||
# Extract values
|
||||
time = $field["_time"]
|
||||
measurement = $field["_measurement"]
|
||||
exchange = $field["exchange"]
|
||||
ticker = $field["ticker"]
|
||||
timeframe = $field["timeframe"]
|
||||
|
||||
# Skip if essential fields are missing
|
||||
if (time == "" || measurement == "" || exchange == "" || ticker == "" || timeframe == "") next
|
||||
|
||||
# Build line protocol
|
||||
# Format: measurement,tag1=value1,tag2=value2 field1=value1,field2=value2 timestamp
|
||||
printf "%s,exchange=%s,ticker=%s,timeframe=%s ", measurement, exchange, ticker, timeframe
|
||||
|
||||
# Add fields
|
||||
first = 1
|
||||
for (fname in field) {
|
||||
if (fname != "_time" && fname != "_start" && fname != "_stop" && fname != "_measurement" &&
|
||||
fname != "exchange" && fname != "ticker" && fname != "timeframe" &&
|
||||
fname != "result" && fname != "table" && fname != "") {
|
||||
val = $field[fname]
|
||||
if (val != "" && val != "NaN") {
|
||||
if (!first) printf ","
|
||||
# Check if value is numeric
|
||||
if (val ~ /^[0-9]+$/) {
|
||||
printf "%s=%si", fname, val
|
||||
} else {
|
||||
printf "%s=%s", fname, val
|
||||
}
|
||||
first = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add timestamp (convert RFC3339 to nanoseconds if needed)
|
||||
printf " %s\n", time
|
||||
}
|
||||
' "$CSV_FILE" > "$TEMP_LP_FILE" 2>/dev/null || {
|
||||
warn "CSV parsing method 1 failed, trying direct import..."
|
||||
|
||||
# Alternative: Use influx write with CSV format directly
|
||||
info "Attempting direct CSV import..."
|
||||
|
||||
if influx write \
|
||||
--host "$INFLUX_URL" \
|
||||
--org "$INFLUX_ORG" \
|
||||
--token "$INFLUX_TOKEN" \
|
||||
--bucket "$BUCKET_NAME" \
|
||||
--format csv \
|
||||
--file "$CSV_FILE" 2>&1; then
|
||||
|
||||
log "✅ Import completed successfully using direct CSV method!"
|
||||
|
||||
echo ""
|
||||
log "📊 Import Summary"
|
||||
echo "============================================"
|
||||
info "Source: $SOURCE_ENV/$SELECTED_EXPORT"
|
||||
info "Target: $TARGET_ENVIRONMENT"
|
||||
info "Bucket: $BUCKET_NAME"
|
||||
log "Status: Success"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
error "Both import methods failed. Please check the error messages above."
|
||||
fi
|
||||
}
|
||||
|
||||
# If line protocol was generated, import it
|
||||
if [ -s "$TEMP_LP_FILE" ]; then
|
||||
LP_LINES=$(wc -l < "$TEMP_LP_FILE" | xargs)
|
||||
info "Generated $LP_LINES lines of line protocol"
|
||||
|
||||
# Import in batches to avoid timeouts
|
||||
BATCH_SIZE=5000
|
||||
TOTAL_LINES=$LP_LINES
|
||||
CURRENT_LINE=0
|
||||
|
||||
info "Importing in batches of $BATCH_SIZE lines..."
|
||||
|
||||
while [ $CURRENT_LINE -lt $TOTAL_LINES ]; do
|
||||
END_LINE=$((CURRENT_LINE + BATCH_SIZE))
|
||||
BATCH_NUM=$((CURRENT_LINE / BATCH_SIZE + 1))
|
||||
PROGRESS=$((CURRENT_LINE * 100 / TOTAL_LINES))
|
||||
|
||||
info "Processing batch $BATCH_NUM (Progress: ${PROGRESS}%)..."
|
||||
|
||||
# Extract batch and import
|
||||
sed -n "$((CURRENT_LINE + 1)),${END_LINE}p" "$TEMP_LP_FILE" | \
|
||||
influx write \
|
||||
--host "$INFLUX_URL" \
|
||||
--org "$INFLUX_ORG" \
|
||||
--token "$INFLUX_TOKEN" \
|
||||
--bucket "$BUCKET_NAME" \
|
||||
--precision s 2>&1 || {
|
||||
warn "Batch $BATCH_NUM had errors, continuing..."
|
||||
}
|
||||
|
||||
CURRENT_LINE=$END_LINE
|
||||
done
|
||||
|
||||
log "✅ Import completed successfully!"
|
||||
else
|
||||
error "Failed to generate line protocol data"
|
||||
fi
|
||||
|
||||
# Final summary
|
||||
echo ""
|
||||
echo "============================================"
|
||||
log "📊 Import Summary"
|
||||
echo "============================================"
|
||||
info "Source: $SOURCE_ENV/$SELECTED_EXPORT"
|
||||
info "Target: $TARGET_ENVIRONMENT"
|
||||
info "Bucket: $BUCKET_NAME"
|
||||
info "File: $(basename "$CSV_FILE")"
|
||||
info "Size: $CSV_SIZE"
|
||||
log "Status: Complete"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
log "🎉 Data successfully imported to $TARGET_ENVIRONMENT!"
|
||||
echo ""
|
||||
info "Verify the import with:"
|
||||
echo " influx query 'from(bucket:\"$BUCKET_NAME\") |> range(start:-1d) |> limit(n:10)' \\"
|
||||
echo " --host \"$INFLUX_URL\" --org \"$INFLUX_ORG\" --token \"$INFLUX_TOKEN\""
|
||||
echo ""
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"Orleans": "Host=kaigen-db.kaigen.managing.live;Port=5433;Database=orleans;Username=postgres;Password=2ab5423dcca4aa2d"
|
||||
},
|
||||
"InfluxDb": {
|
||||
"Url": "https://influx-db.apps.managing.live",
|
||||
"Url": "https://influx-db.kaigen.managing.live",
|
||||
"Organization": "managing-org",
|
||||
"Token": "eOuXcXhH7CS13Iw4CTiDDpRjIjQtEVPOloD82pLPOejI4n0BsEj1YzUw0g3Cs1mdDG5m-RaxCavCMsVTtS5wIQ=="
|
||||
"Token": "ul0pkKXfHutyQgNvJIYaowsCP0cJT_APW2L427sa5DX5ekxlpw43FA_a4_uIc9IG4W99XruRPRxcj2n31XXhZA=="
|
||||
},
|
||||
"RunOrleansGrains": true,
|
||||
"AllowedHosts": "*"
|
||||
|
||||
Reference in New Issue
Block a user