@orchestr-sh/orchestr 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +666 -0
- package/dist/Cache/CacheManager.d.ts +109 -0
- package/dist/Cache/CacheManager.d.ts.map +1 -0
- package/dist/Cache/CacheManager.js +181 -0
- package/dist/Cache/CacheManager.js.map +1 -0
- package/dist/Cache/CacheServiceProvider.d.ts +19 -0
- package/dist/Cache/CacheServiceProvider.d.ts.map +1 -0
- package/dist/Cache/CacheServiceProvider.js +71 -0
- package/dist/Cache/CacheServiceProvider.js.map +1 -0
- package/dist/Cache/Contracts/Lock.d.ts +31 -0
- package/dist/Cache/Contracts/Lock.d.ts.map +1 -0
- package/dist/Cache/Contracts/Lock.js +3 -0
- package/dist/Cache/Contracts/Lock.js.map +1 -0
- package/dist/Cache/Contracts/Repository.d.ts +59 -0
- package/dist/Cache/Contracts/Repository.d.ts.map +1 -0
- package/dist/Cache/Contracts/Repository.js +9 -0
- package/dist/Cache/Contracts/Repository.js.map +1 -0
- package/dist/Cache/Contracts/Store.d.ts +51 -0
- package/dist/Cache/Contracts/Store.d.ts.map +1 -0
- package/dist/Cache/Contracts/Store.js +3 -0
- package/dist/Cache/Contracts/Store.js.map +1 -0
- package/dist/Cache/Events/CacheFlushed.d.ts +11 -0
- package/dist/Cache/Events/CacheFlushed.d.ts.map +1 -0
- package/dist/Cache/Events/CacheFlushed.js +18 -0
- package/dist/Cache/Events/CacheFlushed.js.map +1 -0
- package/dist/Cache/Events/CacheHit.d.ts +13 -0
- package/dist/Cache/Events/CacheHit.d.ts.map +1 -0
- package/dist/Cache/Events/CacheHit.js +22 -0
- package/dist/Cache/Events/CacheHit.js.map +1 -0
- package/dist/Cache/Events/CacheMissed.d.ts +12 -0
- package/dist/Cache/Events/CacheMissed.d.ts.map +1 -0
- package/dist/Cache/Events/CacheMissed.js +20 -0
- package/dist/Cache/Events/CacheMissed.js.map +1 -0
- package/dist/Cache/Events/KeyForgotten.d.ts +12 -0
- package/dist/Cache/Events/KeyForgotten.d.ts.map +1 -0
- package/dist/Cache/Events/KeyForgotten.js +20 -0
- package/dist/Cache/Events/KeyForgotten.js.map +1 -0
- package/dist/Cache/Events/KeyWritten.d.ts +14 -0
- package/dist/Cache/Events/KeyWritten.d.ts.map +1 -0
- package/dist/Cache/Events/KeyWritten.js +24 -0
- package/dist/Cache/Events/KeyWritten.js.map +1 -0
- package/dist/Cache/Events/index.d.ts +6 -0
- package/dist/Cache/Events/index.d.ts.map +1 -0
- package/dist/Cache/Events/index.js +14 -0
- package/dist/Cache/Events/index.js.map +1 -0
- package/dist/Cache/Locks/CacheLock.d.ts +19 -0
- package/dist/Cache/Locks/CacheLock.d.ts.map +1 -0
- package/dist/Cache/Locks/CacheLock.js +49 -0
- package/dist/Cache/Locks/CacheLock.js.map +1 -0
- package/dist/Cache/Locks/Lock.d.ts +51 -0
- package/dist/Cache/Locks/Lock.d.ts.map +1 -0
- package/dist/Cache/Locks/Lock.js +83 -0
- package/dist/Cache/Locks/Lock.js.map +1 -0
- package/dist/Cache/Locks/LockTimeoutException.d.ts +9 -0
- package/dist/Cache/Locks/LockTimeoutException.d.ts.map +1 -0
- package/dist/Cache/Locks/LockTimeoutException.js +16 -0
- package/dist/Cache/Locks/LockTimeoutException.js.map +1 -0
- package/dist/Cache/Locks/index.d.ts +4 -0
- package/dist/Cache/Locks/index.d.ts.map +1 -0
- package/dist/Cache/Locks/index.js +10 -0
- package/dist/Cache/Locks/index.js.map +1 -0
- package/dist/Cache/Repository.d.ts +61 -0
- package/dist/Cache/Repository.d.ts.map +1 -0
- package/dist/Cache/Repository.js +207 -0
- package/dist/Cache/Repository.js.map +1 -0
- package/dist/Cache/Stores/ArrayStore.d.ts +44 -0
- package/dist/Cache/Stores/ArrayStore.d.ts.map +1 -0
- package/dist/Cache/Stores/ArrayStore.js +118 -0
- package/dist/Cache/Stores/ArrayStore.js.map +1 -0
- package/dist/Cache/Stores/DatabaseStore.d.ts +44 -0
- package/dist/Cache/Stores/DatabaseStore.d.ts.map +1 -0
- package/dist/Cache/Stores/DatabaseStore.js +165 -0
- package/dist/Cache/Stores/DatabaseStore.js.map +1 -0
- package/dist/Cache/Stores/FileStore.d.ts +50 -0
- package/dist/Cache/Stores/FileStore.d.ts.map +1 -0
- package/dist/Cache/Stores/FileStore.js +194 -0
- package/dist/Cache/Stores/FileStore.js.map +1 -0
- package/dist/Cache/Stores/NullStore.d.ts +22 -0
- package/dist/Cache/Stores/NullStore.d.ts.map +1 -0
- package/dist/Cache/Stores/NullStore.js +49 -0
- package/dist/Cache/Stores/NullStore.js.map +1 -0
- package/dist/Cache/Stores/index.d.ts +5 -0
- package/dist/Cache/Stores/index.d.ts.map +1 -0
- package/dist/Cache/Stores/index.js +12 -0
- package/dist/Cache/Stores/index.js.map +1 -0
- package/dist/Cache/Tags/TagSet.d.ts +39 -0
- package/dist/Cache/Tags/TagSet.d.ts.map +1 -0
- package/dist/Cache/Tags/TagSet.js +72 -0
- package/dist/Cache/Tags/TagSet.js.map +1 -0
- package/dist/Cache/Tags/TaggedCache.d.ts +54 -0
- package/dist/Cache/Tags/TaggedCache.d.ts.map +1 -0
- package/dist/Cache/Tags/TaggedCache.js +125 -0
- package/dist/Cache/Tags/TaggedCache.js.map +1 -0
- package/dist/Cache/Tags/index.d.ts +3 -0
- package/dist/Cache/Tags/index.d.ts.map +1 -0
- package/dist/Cache/Tags/index.js +8 -0
- package/dist/Cache/Tags/index.js.map +1 -0
- package/dist/Cache/index.d.ts +27 -0
- package/dist/Cache/index.d.ts.map +1 -0
- package/dist/Cache/index.js +48 -0
- package/dist/Cache/index.js.map +1 -0
- package/dist/Console/Commands/CacheClearCommand.d.ts +16 -0
- package/dist/Console/Commands/CacheClearCommand.d.ts.map +1 -0
- package/dist/Console/Commands/CacheClearCommand.js +33 -0
- package/dist/Console/Commands/CacheClearCommand.js.map +1 -0
- package/dist/Console/Commands/CacheForgetCommand.d.ts +16 -0
- package/dist/Console/Commands/CacheForgetCommand.d.ts.map +1 -0
- package/dist/Console/Commands/CacheForgetCommand.js +39 -0
- package/dist/Console/Commands/CacheForgetCommand.js.map +1 -0
- package/dist/Console/Commands/CacheTableCommand.d.ts +17 -0
- package/dist/Console/Commands/CacheTableCommand.d.ts.map +1 -0
- package/dist/Console/Commands/CacheTableCommand.js +92 -0
- package/dist/Console/Commands/CacheTableCommand.js.map +1 -0
- package/dist/Facades/Cache.d.ts +41 -0
- package/dist/Facades/Cache.d.ts.map +1 -0
- package/dist/Facades/Cache.js +78 -0
- package/dist/Facades/Cache.js.map +1 -0
- package/dist/Facades/index.d.ts +1 -0
- package/dist/Facades/index.d.ts.map +1 -1
- package/dist/Facades/index.js +3 -1
- package/dist/Facades/index.js.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -858,6 +858,670 @@ npx orchestr make:listener <name> --queued # Create queued listener
|
|
|
858
858
|
npx orchestr event:list # List all registered events
|
|
859
859
|
npx orchestr event:cache # Cache discovered events
|
|
860
860
|
npx orchestr event:clear # Clear event cache
|
|
861
|
+
|
|
862
|
+
# Queue - Jobs
|
|
863
|
+
npx orchestr make:job <name> # Create job class
|
|
864
|
+
npx orchestr make:job <name> --sync # Create synchronous job
|
|
865
|
+
|
|
866
|
+
# Queue - Workers
|
|
867
|
+
npx orchestr queue:work [connection] # Start queue worker daemon
|
|
868
|
+
npx orchestr queue:work --once # Process single job and exit
|
|
869
|
+
npx orchestr queue:work --queue=high,default # Process specific queues
|
|
870
|
+
npx orchestr queue:work --tries=3 # Set max attempts
|
|
871
|
+
npx orchestr queue:work --timeout=90 # Set job timeout
|
|
872
|
+
npx orchestr queue:work --sleep=3 # Seconds between checks
|
|
873
|
+
npx orchestr queue:work --max-jobs=1000 # Stop after N jobs
|
|
874
|
+
npx orchestr queue:work --max-time=3600 # Stop after N seconds
|
|
875
|
+
npx orchestr queue:work --memory=128 # Memory limit in MB
|
|
876
|
+
npx orchestr queue:work --rest=0 # Rest between jobs (ms)
|
|
877
|
+
npx orchestr queue:work --stop-when-empty # Stop when queue is empty
|
|
878
|
+
npx orchestr queue:restart # Gracefully restart all workers
|
|
879
|
+
|
|
880
|
+
# Queue - Failed Jobs
|
|
881
|
+
npx orchestr queue:failed # List all failed jobs
|
|
882
|
+
npx orchestr queue:retry <id> # Retry specific failed job
|
|
883
|
+
npx orchestr queue:retry all # Retry all failed jobs
|
|
884
|
+
npx orchestr queue:forget <id> # Delete failed job by ID
|
|
885
|
+
npx orchestr queue:flush # Delete all failed jobs
|
|
886
|
+
npx orchestr queue:prune-failed --hours=48 # Prune failed jobs older than N hours
|
|
887
|
+
|
|
888
|
+
# Queue - Monitoring
|
|
889
|
+
npx orchestr queue:monitor <queue> --max=100 # Alert if queue exceeds size
|
|
890
|
+
npx orchestr queue:clear [connection] --queue=default # Clear queue
|
|
891
|
+
|
|
892
|
+
# Queue - Database Setup
|
|
893
|
+
npx orchestr queue:table # Create jobs table migration
|
|
894
|
+
npx orchestr queue:failed-table # Create failed_jobs table migration
|
|
895
|
+
npx orchestr queue:batches-table # Create job_batches table migration
|
|
896
|
+
npx orchestr queue:prune-batches --hours=24 # Prune batches older than N hours
|
|
897
|
+
|
|
898
|
+
# Cache
|
|
899
|
+
npx orchestr cache:clear [--store=redis] # Clear all cache or specific store
|
|
900
|
+
npx orchestr cache:forget <key> [--store=redis] # Forget specific cache key
|
|
901
|
+
npx orchestr cache:table # Create cache table migration
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
## Queue System
|
|
905
|
+
|
|
906
|
+
Orchestr provides a powerful queue system for deferring time-intensive tasks. Jobs can be pushed to various drivers (sync, database), retried on failure, organized into chains and batches, and monitored through a comprehensive CLI.
|
|
907
|
+
|
|
908
|
+
### Configuration
|
|
909
|
+
|
|
910
|
+
Create a queue configuration in your application:
|
|
911
|
+
|
|
912
|
+
```typescript
|
|
913
|
+
import { QueueServiceProvider } from '@orchestr-sh/orchestr';
|
|
914
|
+
|
|
915
|
+
app.register(new QueueServiceProvider(app));
|
|
916
|
+
|
|
917
|
+
// Configure in ConfigServiceProvider
|
|
918
|
+
{
|
|
919
|
+
queue: {
|
|
920
|
+
default: 'database',
|
|
921
|
+
connections: {
|
|
922
|
+
sync: {
|
|
923
|
+
driver: 'sync',
|
|
924
|
+
},
|
|
925
|
+
database: {
|
|
926
|
+
driver: 'database',
|
|
927
|
+
table: 'jobs',
|
|
928
|
+
queue: 'default',
|
|
929
|
+
retry_after: 90,
|
|
930
|
+
after_commit: false,
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
failed: {
|
|
934
|
+
driver: 'database',
|
|
935
|
+
database: 'sqlite',
|
|
936
|
+
table: 'failed_jobs',
|
|
937
|
+
},
|
|
938
|
+
batching: {
|
|
939
|
+
database: 'sqlite',
|
|
940
|
+
table: 'job_batches',
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### Creating Jobs
|
|
947
|
+
|
|
948
|
+
Jobs are classes that extend the `Job` base class:
|
|
949
|
+
|
|
950
|
+
```typescript
|
|
951
|
+
import { Job } from '@orchestr-sh/orchestr';
|
|
952
|
+
|
|
953
|
+
export class ProcessPodcast extends Job {
|
|
954
|
+
public tries = 5;
|
|
955
|
+
public timeout = 120;
|
|
956
|
+
public backoff = [10, 30, 60];
|
|
957
|
+
|
|
958
|
+
constructor(public podcastId: number) {
|
|
959
|
+
super();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
async handle(): Promise<void> {
|
|
963
|
+
// Process the podcast
|
|
964
|
+
const podcast = await Podcast.find(this.podcastId);
|
|
965
|
+
await podcast.process();
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
async failed(error: Error): Promise<void> {
|
|
969
|
+
// Handle job failure
|
|
970
|
+
console.error(`Failed to process podcast ${this.podcastId}:`, error);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Create via CLI
|
|
975
|
+
npx orchestr make:job ProcessPodcast
|
|
976
|
+
npx orchestr make:job SendEmail --sync // Synchronous job
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Dispatching Jobs
|
|
980
|
+
|
|
981
|
+
```typescript
|
|
982
|
+
// Basic dispatch
|
|
983
|
+
await ProcessPodcast.dispatch(podcastId);
|
|
984
|
+
|
|
985
|
+
// Fluent dispatch API
|
|
986
|
+
await ProcessPodcast.dispatch(podcastId)
|
|
987
|
+
.onQueue('high-priority')
|
|
988
|
+
.onConnection('redis')
|
|
989
|
+
.delay(60)
|
|
990
|
+
.tries(3)
|
|
991
|
+
.timeout(300)
|
|
992
|
+
.backoff([30, 60, 120]);
|
|
993
|
+
|
|
994
|
+
// Conditional dispatch
|
|
995
|
+
await ProcessPodcast.dispatchIf(podcast.needsProcessing, podcastId);
|
|
996
|
+
await ProcessPodcast.dispatchUnless(podcast.isProcessed, podcastId);
|
|
997
|
+
|
|
998
|
+
// Synchronous dispatch (runs immediately)
|
|
999
|
+
await ProcessPodcast.dispatchSync(podcastId);
|
|
1000
|
+
|
|
1001
|
+
// Using Queue/Bus facades
|
|
1002
|
+
import { Queue, Bus } from '@orchestr-sh/orchestr';
|
|
1003
|
+
|
|
1004
|
+
await Queue.push(new ProcessPodcast(podcastId));
|
|
1005
|
+
await Queue.pushOn('high-priority', new ProcessPodcast(podcastId));
|
|
1006
|
+
await Queue.later(60, new ProcessPodcast(podcastId));
|
|
1007
|
+
|
|
1008
|
+
await Bus.dispatch(new ProcessPodcast(podcastId));
|
|
1009
|
+
await Bus.dispatchSync(new ProcessPodcast(podcastId));
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
### Job Middleware
|
|
1013
|
+
|
|
1014
|
+
Middleware can be applied to jobs for rate limiting, preventing overlaps, and throttling exceptions:
|
|
1015
|
+
|
|
1016
|
+
```typescript
|
|
1017
|
+
import { RateLimited, WithoutOverlapping, ThrottlesExceptions } from '@orchestr-sh/orchestr';
|
|
1018
|
+
|
|
1019
|
+
export class ProcessPodcast extends Job {
|
|
1020
|
+
constructor(public podcastId: number) {
|
|
1021
|
+
super();
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
middleware() {
|
|
1025
|
+
return [
|
|
1026
|
+
// Allow 10 jobs per minute
|
|
1027
|
+
new RateLimited('podcasts', 10, 60).releaseAfter(30),
|
|
1028
|
+
|
|
1029
|
+
// Prevent overlapping jobs for the same podcast
|
|
1030
|
+
new WithoutOverlapping(this.podcastId)
|
|
1031
|
+
.releaseAfter(10)
|
|
1032
|
+
.expireAfter(300),
|
|
1033
|
+
|
|
1034
|
+
// Throttle on too many exceptions
|
|
1035
|
+
new ThrottlesExceptions(5, 10).backoff(5),
|
|
1036
|
+
];
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
async handle(): Promise<void> {
|
|
1040
|
+
// Process podcast
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### Job Chaining
|
|
1046
|
+
|
|
1047
|
+
Execute jobs sequentially - if one fails, the chain stops:
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import { Bus } from '@orchestr-sh/orchestr';
|
|
1051
|
+
|
|
1052
|
+
await Bus.chain([
|
|
1053
|
+
new ProcessPodcast(podcastId),
|
|
1054
|
+
new OptimizePodcast(podcastId),
|
|
1055
|
+
new PublishPodcast(podcastId),
|
|
1056
|
+
new NotifySubscribers(podcastId),
|
|
1057
|
+
])
|
|
1058
|
+
.onQueue('processing')
|
|
1059
|
+
.onConnection('redis')
|
|
1060
|
+
.delay(300)
|
|
1061
|
+
.catch((error) => {
|
|
1062
|
+
console.error('Chain failed:', error);
|
|
1063
|
+
})
|
|
1064
|
+
.dispatch();
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
### Job Batching
|
|
1068
|
+
|
|
1069
|
+
Execute jobs concurrently with progress tracking:
|
|
1070
|
+
|
|
1071
|
+
```typescript
|
|
1072
|
+
import { Bus } from '@orchestr-sh/orchestr';
|
|
1073
|
+
|
|
1074
|
+
const batch = await Bus.batch([
|
|
1075
|
+
new ImportRow(1),
|
|
1076
|
+
new ImportRow(2),
|
|
1077
|
+
new ImportRow(3),
|
|
1078
|
+
new ImportRow(4),
|
|
1079
|
+
])
|
|
1080
|
+
.name('CSV Import')
|
|
1081
|
+
.then((batch) => {
|
|
1082
|
+
console.log('All imports complete!');
|
|
1083
|
+
})
|
|
1084
|
+
.catch((batch, error) => {
|
|
1085
|
+
console.error('A job failed:', error);
|
|
1086
|
+
})
|
|
1087
|
+
.finally((batch) => {
|
|
1088
|
+
console.log(`Processed ${batch.processedJobs}/${batch.totalJobs} jobs`);
|
|
1089
|
+
})
|
|
1090
|
+
.allowFailures()
|
|
1091
|
+
.onQueue('imports')
|
|
1092
|
+
.dispatch();
|
|
1093
|
+
|
|
1094
|
+
// Check batch progress
|
|
1095
|
+
console.log(`Progress: ${batch.progress()}%`);
|
|
1096
|
+
console.log(`Pending: ${batch.pendingJobs}`);
|
|
1097
|
+
console.log(`Failed: ${batch.failedJobs}`);
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
### Running Workers
|
|
1101
|
+
|
|
1102
|
+
Process queued jobs with the worker daemon:
|
|
1103
|
+
|
|
1104
|
+
```bash
|
|
1105
|
+
# Start a worker
|
|
1106
|
+
npx orchestr queue:work
|
|
1107
|
+
|
|
1108
|
+
# Specify connection and queue
|
|
1109
|
+
npx orchestr queue:work database --queue=high-priority,default
|
|
1110
|
+
|
|
1111
|
+
# Worker options
|
|
1112
|
+
npx orchestr queue:work database \
|
|
1113
|
+
--queue=high,default \
|
|
1114
|
+
--tries=3 \
|
|
1115
|
+
--timeout=90 \
|
|
1116
|
+
--sleep=3 \
|
|
1117
|
+
--max-jobs=1000 \
|
|
1118
|
+
--max-time=3600 \
|
|
1119
|
+
--memory=128 \
|
|
1120
|
+
--rest=0
|
|
1121
|
+
|
|
1122
|
+
# Process a single job (--once)
|
|
1123
|
+
npx orchestr queue:work --once
|
|
1124
|
+
|
|
1125
|
+
# Stop when queue is empty
|
|
1126
|
+
npx orchestr queue:work --stop-when-empty
|
|
1127
|
+
|
|
1128
|
+
# Restart all workers gracefully
|
|
1129
|
+
npx orchestr queue:restart
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
### Failed Jobs
|
|
1133
|
+
|
|
1134
|
+
Manage failed jobs through the CLI or programmatically:
|
|
1135
|
+
|
|
1136
|
+
```bash
|
|
1137
|
+
# List all failed jobs
|
|
1138
|
+
npx orchestr queue:failed
|
|
1139
|
+
|
|
1140
|
+
# Retry a specific failed job
|
|
1141
|
+
npx orchestr queue:retry 5
|
|
1142
|
+
|
|
1143
|
+
# Retry all failed jobs
|
|
1144
|
+
npx orchestr queue:retry all
|
|
1145
|
+
|
|
1146
|
+
# Forget (delete) a failed job
|
|
1147
|
+
npx orchestr queue:forget 5
|
|
1148
|
+
|
|
1149
|
+
# Flush all failed jobs
|
|
1150
|
+
npx orchestr queue:flush
|
|
1151
|
+
|
|
1152
|
+
# Prune failed jobs older than 48 hours
|
|
1153
|
+
npx orchestr queue:prune-failed --hours=48
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
### Queue Monitoring
|
|
1157
|
+
|
|
1158
|
+
```bash
|
|
1159
|
+
# Monitor queue for jobs exceeding thresholds
|
|
1160
|
+
npx orchestr queue:monitor database:default --max=100
|
|
1161
|
+
|
|
1162
|
+
# Clear all jobs from a queue
|
|
1163
|
+
npx orchestr queue:clear database --queue=default
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
### Database Setup
|
|
1167
|
+
|
|
1168
|
+
```bash
|
|
1169
|
+
# Create queue tables migration
|
|
1170
|
+
npx orchestr queue:table
|
|
1171
|
+
|
|
1172
|
+
# Create failed jobs table migration
|
|
1173
|
+
npx orchestr queue:failed-table
|
|
1174
|
+
|
|
1175
|
+
# Create job batches table migration
|
|
1176
|
+
npx orchestr queue:batches-table
|
|
1177
|
+
|
|
1178
|
+
# Run migrations
|
|
1179
|
+
npx orchestr migrate
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
### Job Events
|
|
1183
|
+
|
|
1184
|
+
Register callbacks for job lifecycle events:
|
|
1185
|
+
|
|
1186
|
+
```typescript
|
|
1187
|
+
import { Queue } from '@orchestr-sh/orchestr';
|
|
1188
|
+
|
|
1189
|
+
// Before job processing
|
|
1190
|
+
Queue.before((connectionName, job) => {
|
|
1191
|
+
console.log(`Processing: ${job.displayName()}`);
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
// After job processing
|
|
1195
|
+
Queue.after((connectionName, job) => {
|
|
1196
|
+
console.log(`Completed: ${job.displayName()}`);
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// When a job fails
|
|
1200
|
+
Queue.failing((connectionName, job, error) => {
|
|
1201
|
+
console.error(`Failed: ${job.displayName()}`, error);
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
// On each worker loop iteration
|
|
1205
|
+
Queue.looping(() => {
|
|
1206
|
+
// Perform maintenance tasks
|
|
1207
|
+
});
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### Custom Queue Drivers
|
|
1211
|
+
|
|
1212
|
+
Extend the queue system with custom drivers:
|
|
1213
|
+
|
|
1214
|
+
```typescript
|
|
1215
|
+
import { QueueDriver } from '@orchestr-sh/orchestr';
|
|
1216
|
+
|
|
1217
|
+
class RedisDriver implements QueueDriver {
|
|
1218
|
+
async push(job: Job, queue?: string): Promise<string> {
|
|
1219
|
+
// Push job to Redis
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
async pop(queue?: string): Promise<QueueDriverJob | null> {
|
|
1223
|
+
// Pop job from Redis
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Implement other methods...
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Register the driver
|
|
1230
|
+
const manager = app.make<QueueManager>('queue');
|
|
1231
|
+
manager.registerDriver('redis', (config) => new RedisDriver(config));
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
## Cache System
|
|
1235
|
+
|
|
1236
|
+
Orchestr provides a flexible caching system with multiple drivers, tags, locks, and stale-while-revalidate support.
|
|
1237
|
+
|
|
1238
|
+
### Configuration
|
|
1239
|
+
|
|
1240
|
+
Configure cache stores in your application:
|
|
1241
|
+
|
|
1242
|
+
```typescript
|
|
1243
|
+
import { CacheServiceProvider } from '@orchestr-sh/orchestr';
|
|
1244
|
+
|
|
1245
|
+
app.register(new CacheServiceProvider(app));
|
|
1246
|
+
|
|
1247
|
+
// Configure in ConfigServiceProvider
|
|
1248
|
+
{
|
|
1249
|
+
cache: {
|
|
1250
|
+
default: 'file',
|
|
1251
|
+
prefix: 'app_cache_',
|
|
1252
|
+
stores: {
|
|
1253
|
+
array: {
|
|
1254
|
+
driver: 'array',
|
|
1255
|
+
serialize: false,
|
|
1256
|
+
},
|
|
1257
|
+
file: {
|
|
1258
|
+
driver: 'file',
|
|
1259
|
+
path: 'storage/framework/cache/data',
|
|
1260
|
+
},
|
|
1261
|
+
database: {
|
|
1262
|
+
driver: 'database',
|
|
1263
|
+
connection: null,
|
|
1264
|
+
table: 'cache',
|
|
1265
|
+
},
|
|
1266
|
+
null: {
|
|
1267
|
+
driver: 'null',
|
|
1268
|
+
},
|
|
1269
|
+
},
|
|
1270
|
+
},
|
|
1271
|
+
}
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
### Basic Usage
|
|
1275
|
+
|
|
1276
|
+
```typescript
|
|
1277
|
+
import { Cache } from '@orchestr-sh/orchestr';
|
|
1278
|
+
|
|
1279
|
+
// Store items
|
|
1280
|
+
await Cache.put('key', 'value', 3600); // TTL in seconds
|
|
1281
|
+
await Cache.put('key', 'value', new Date('2024-12-31')); // TTL as Date
|
|
1282
|
+
await Cache.forever('key', 'value'); // Store forever
|
|
1283
|
+
|
|
1284
|
+
// Retrieve items
|
|
1285
|
+
const value = await Cache.get('key');
|
|
1286
|
+
const value = await Cache.get('key', 'default value');
|
|
1287
|
+
const value = await Cache.get('key', () => 'computed default');
|
|
1288
|
+
|
|
1289
|
+
// Multiple items
|
|
1290
|
+
await Cache.putMany({ key1: 'value1', key2: 'value2' }, 3600);
|
|
1291
|
+
const values = await Cache.many(['key1', 'key2']);
|
|
1292
|
+
|
|
1293
|
+
// Check existence
|
|
1294
|
+
if (await Cache.has('key')) {
|
|
1295
|
+
// Key exists
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (await Cache.missing('key')) {
|
|
1299
|
+
// Key does not exist
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// Remove items
|
|
1303
|
+
await Cache.forget('key');
|
|
1304
|
+
await Cache.flush(); // Clear all cache
|
|
1305
|
+
|
|
1306
|
+
// Retrieve and delete
|
|
1307
|
+
const value = await Cache.pull('key', 'default');
|
|
1308
|
+
```
|
|
1309
|
+
|
|
1310
|
+
### Remember Pattern
|
|
1311
|
+
|
|
1312
|
+
Cache the result of expensive operations:
|
|
1313
|
+
|
|
1314
|
+
```typescript
|
|
1315
|
+
// Cache for 1 hour if not exists
|
|
1316
|
+
const user = await Cache.remember('user:1', 3600, async () => {
|
|
1317
|
+
return await User.find(1);
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
// Cache forever if not exists
|
|
1321
|
+
const settings = await Cache.rememberForever('settings', async () => {
|
|
1322
|
+
return await fetchSettings();
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
// Add only if key doesn't exist
|
|
1326
|
+
await Cache.add('key', 'value', 3600);
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
### Flexible Caching (Stale-While-Revalidate)
|
|
1330
|
+
|
|
1331
|
+
Serve stale content while revalidating in the background:
|
|
1332
|
+
|
|
1333
|
+
```typescript
|
|
1334
|
+
// Fresh for 30s, stale for up to 300s
|
|
1335
|
+
const data = await Cache.flexible('api:data', [30, 300], async () => {
|
|
1336
|
+
return await fetchFromAPI();
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
// First 30 seconds: serves fresh data
|
|
1340
|
+
// After 30 seconds: serves stale data, triggers background refresh
|
|
1341
|
+
// After 300 seconds: fetches fresh data synchronously
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
### Incrementing and Decrementing
|
|
1345
|
+
|
|
1346
|
+
```typescript
|
|
1347
|
+
// Increment
|
|
1348
|
+
await Cache.increment('views'); // +1
|
|
1349
|
+
await Cache.increment('views', 5); // +5
|
|
1350
|
+
|
|
1351
|
+
// Decrement
|
|
1352
|
+
await Cache.decrement('views'); // -1
|
|
1353
|
+
await Cache.decrement('views', 3); // -3
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
### Multiple Stores
|
|
1357
|
+
|
|
1358
|
+
Switch between different cache stores:
|
|
1359
|
+
|
|
1360
|
+
```typescript
|
|
1361
|
+
// Use specific store
|
|
1362
|
+
await Cache.store('redis').put('key', 'value', 3600);
|
|
1363
|
+
await Cache.store('file').put('key', 'value', 3600);
|
|
1364
|
+
await Cache.store('database').put('key', 'value', 3600);
|
|
1365
|
+
|
|
1366
|
+
// Chain operations
|
|
1367
|
+
const value = await Cache.store('redis').remember('expensive', 3600, async () => {
|
|
1368
|
+
return await computeExpensiveValue();
|
|
1369
|
+
});
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
### Cache Tags
|
|
1373
|
+
|
|
1374
|
+
Group related cache entries for easy invalidation:
|
|
1375
|
+
|
|
1376
|
+
```typescript
|
|
1377
|
+
// Store tagged items
|
|
1378
|
+
await Cache.tags(['people', 'artists']).put('John', johnData, 3600);
|
|
1379
|
+
await Cache.tags(['people', 'authors']).put('Anne', anneData, 3600);
|
|
1380
|
+
await Cache.tags('products').put('product:1', productData, 3600);
|
|
1381
|
+
|
|
1382
|
+
// Retrieve tagged items
|
|
1383
|
+
const john = await Cache.tags(['people', 'artists']).get('John');
|
|
1384
|
+
|
|
1385
|
+
// Flush by tag (removes all tagged entries)
|
|
1386
|
+
await Cache.tags('people').flush(); // Removes John and Anne
|
|
1387
|
+
await Cache.tags(['people', 'artists']).flush();
|
|
1388
|
+
|
|
1389
|
+
// Remember with tags
|
|
1390
|
+
const user = await Cache.tags(['users', 'premium']).remember('user:1', 3600, async () => {
|
|
1391
|
+
return await User.find(1);
|
|
1392
|
+
});
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
### Cache Locks
|
|
1396
|
+
|
|
1397
|
+
Atomic locks for preventing race conditions:
|
|
1398
|
+
|
|
1399
|
+
```typescript
|
|
1400
|
+
// Basic lock usage
|
|
1401
|
+
const lock = Cache.lock('processing', 120);
|
|
1402
|
+
|
|
1403
|
+
if (await lock.get()) {
|
|
1404
|
+
try {
|
|
1405
|
+
// Critical section
|
|
1406
|
+
await processExpensiveOperation();
|
|
1407
|
+
} finally {
|
|
1408
|
+
await lock.release();
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// Auto-release with callback
|
|
1413
|
+
await Cache.lock('processing', 120).get(async () => {
|
|
1414
|
+
// Lock is automatically released after callback
|
|
1415
|
+
await processExpensiveOperation();
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
// Block until lock is acquired
|
|
1419
|
+
try {
|
|
1420
|
+
await Cache.lock('processing', 120).block(10, async () => {
|
|
1421
|
+
// Wait up to 10 seconds for lock
|
|
1422
|
+
await processExpensiveOperation();
|
|
1423
|
+
});
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
// LockTimeoutException after 10 seconds
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// Check lock ownership
|
|
1429
|
+
const lock = Cache.lock('processing', 120);
|
|
1430
|
+
await lock.get();
|
|
1431
|
+
|
|
1432
|
+
if (await lock.isOwnedByCurrentProcess()) {
|
|
1433
|
+
await lock.release();
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// Force release (ignores ownership)
|
|
1437
|
+
await lock.forceRelease();
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
### Database Setup
|
|
1441
|
+
|
|
1442
|
+
```bash
|
|
1443
|
+
# Create cache table migration
|
|
1444
|
+
npx orchestr cache:table
|
|
1445
|
+
|
|
1446
|
+
# Run migration
|
|
1447
|
+
npx orchestr migrate
|
|
1448
|
+
```
|
|
1449
|
+
|
|
1450
|
+
### Cache Commands
|
|
1451
|
+
|
|
1452
|
+
```bash
|
|
1453
|
+
# Clear all cache
|
|
1454
|
+
npx orchestr cache:clear
|
|
1455
|
+
|
|
1456
|
+
# Clear specific store
|
|
1457
|
+
npx orchestr cache:clear --store=redis
|
|
1458
|
+
|
|
1459
|
+
# Forget a specific key
|
|
1460
|
+
npx orchestr cache:forget key-name
|
|
1461
|
+
|
|
1462
|
+
# Forget from specific store
|
|
1463
|
+
npx orchestr cache:forget key-name --store=redis
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
### Custom Cache Drivers
|
|
1467
|
+
|
|
1468
|
+
Create custom cache drivers for Redis, Memcached, etc.:
|
|
1469
|
+
|
|
1470
|
+
```typescript
|
|
1471
|
+
import { Store } from '@orchestr-sh/orchestr';
|
|
1472
|
+
|
|
1473
|
+
class RedisStore implements Store {
|
|
1474
|
+
async get(key: string): Promise<any> {
|
|
1475
|
+
// Get from Redis
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
async put(key: string, value: any, seconds: number): Promise<boolean> {
|
|
1479
|
+
// Put to Redis
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// Implement other methods...
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Register the driver
|
|
1486
|
+
const manager = app.make<CacheManager>('cache');
|
|
1487
|
+
manager.registerDriver('redis', (config) => new RedisStore(config));
|
|
1488
|
+
```
|
|
1489
|
+
|
|
1490
|
+
### Repository Methods
|
|
1491
|
+
|
|
1492
|
+
The cache repository provides these methods:
|
|
1493
|
+
|
|
1494
|
+
```typescript
|
|
1495
|
+
// Basic operations
|
|
1496
|
+
await Cache.get(key, defaultValue?)
|
|
1497
|
+
await Cache.many(keys)
|
|
1498
|
+
await Cache.put(key, value, ttl?)
|
|
1499
|
+
await Cache.putMany(values, ttl?)
|
|
1500
|
+
await Cache.forever(key, value)
|
|
1501
|
+
await Cache.forget(key)
|
|
1502
|
+
await Cache.flush()
|
|
1503
|
+
|
|
1504
|
+
// High-level operations
|
|
1505
|
+
await Cache.has(key)
|
|
1506
|
+
await Cache.missing(key)
|
|
1507
|
+
await Cache.pull(key, defaultValue?)
|
|
1508
|
+
await Cache.add(key, value, ttl?)
|
|
1509
|
+
await Cache.remember(key, ttl, callback)
|
|
1510
|
+
await Cache.rememberForever(key, callback)
|
|
1511
|
+
await Cache.flexible(key, [freshTtl, staleTtl], callback)
|
|
1512
|
+
|
|
1513
|
+
// Numeric operations
|
|
1514
|
+
await Cache.increment(key, value?)
|
|
1515
|
+
await Cache.decrement(key, value?)
|
|
1516
|
+
|
|
1517
|
+
// Tags and locks
|
|
1518
|
+
Cache.tags(names)
|
|
1519
|
+
Cache.lock(name, seconds?, owner?)
|
|
1520
|
+
Cache.restoreLock(name, owner)
|
|
1521
|
+
|
|
1522
|
+
// Store operations
|
|
1523
|
+
Cache.store(name?)
|
|
1524
|
+
Cache.getPrefix()
|
|
861
1525
|
```
|
|
862
1526
|
|
|
863
1527
|
## Features
|
|
@@ -877,6 +1541,8 @@ npx orchestr event:clear # Clear event cache
|
|
|
877
1541
|
- ✅ Event Subscribers
|
|
878
1542
|
- ✅ Model Lifecycle Events
|
|
879
1543
|
- ✅ Event Testing (Fakes & Assertions)
|
|
1544
|
+
- ✅ Queue System (Jobs, Chains, Batches, Workers)
|
|
1545
|
+
- ✅ Cache System (Tags, Locks, Flexible Caching)
|
|
880
1546
|
- ✅ Soft Deletes
|
|
881
1547
|
- ✅ Attribute Casting
|
|
882
1548
|
- ✅ Timestamps
|