Repository navigation
Expand file tree
/
Copy pathBatchProcessing.java
More file actions
93 lines (82 loc) · 3.77 KB
/
Copy pathBatchProcessing.java
File metadata and controls
93 lines (82 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package org.vedastro.demos;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.vedastro.GeoLocation;
import org.vedastro.Time;
import org.vedastro.VedAstroClient;
import org.vedastro.calculate.PlanetName;
/**
* DEMO: Batch processing many charts from a server.
*
* <p>USE CASE: look up many charts without tripping the rate limit, using patterns a server
* actually needs
* <p>DIFFICULTY: Advanced
*
* <p>RUN: {@code mvn -q exec:java -Dexec.mainClass=org.vedastro.demos.BatchProcessing}
*/
public final class BatchProcessing {
/** One chart to look up. */
private record Person(String name, String time, double longitude, double latitude) {
}
private static final List<Person> PEOPLE = List.of(
new Person("Mumbai", "14:30 25/10/1992 +05:30", 72.8777, 19.0760),
new Person("Tokyo", "23:40 31/12/2010 +08:00", 139.83, 35.65),
new Person("New York", "09:00 15/06/1997 -05:00", -74.006, 40.7128)
);
/**
* Spaces calls evenly so the key's rate limit is never reached.
*
* <p>The limit is counted per API key across every thread and process, so a thread pool
* alone does not help: unsynchronised calls simply get rejected. This throttle serialises
* the <em>start</em> of each call, not the calls themselves, so the pool still overlaps
* the waiting on the network.
*/
private static final class Throttle {
private final long gapMillis;
private long nextAllowedAt;
Throttle(int callsPerMinute) {
this.gapMillis = (long) Math.ceil(60_000.0 / callsPerMinute);
}
synchronized void acquire() throws InterruptedException {
long now = System.currentTimeMillis();
long waitFor = nextAllowedAt - now;
nextAllowedAt = Math.max(now, nextAllowedAt) + gapMillis;
if (waitFor > 0) {
Thread.sleep(waitFor);
}
}
}
private BatchProcessing() {
}
public static void main(String[] args) throws Exception {
VedAstroClient client = DemoSupport.client();
// Five calls a minute on the free tier; paid keys allow far more.
Throttle throttle = new Throttle(DemoSupport.freeTier() ? 5 : 200);
// Virtual threads (Java 21) suit this work: every call blocks on the network, and each
// job gets a lightweight thread instead of occupying a platform thread.
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> results = new ArrayList<>();
for (Person person : PEOPLE) {
results.add(pool.submit(() -> {
throttle.acquire();
GeoLocation place = GeoLocation.of(person.name(), person.longitude(), person.latitude());
Time birth = Time.of(person.time(), place);
Object sun = client.calculate().planetRasiD1Sign(PlanetName.SUN, birth);
return person.name() + " -> Sun in " + DemoSupport.field(sun, "Name");
}));
}
for (Future<String> result : results) {
System.out.println(result.get());
}
}
// SERVING THIS FROM A WEB SERVER
// - Build one VedAstroClient at start-up and inject it; never create one per request.
// - Give each request its own thread or virtual thread; calls block by design.
// - Use client.withAyanamsa(...) for a per-request ayanamsa with no shared mutable state.
// - Cache by chart input: the same birth details recur, and a cache hit costs nothing.
// - Retry only idempotent reads, and only on transport failures.
}
}