Skip to content

Commit 5286064

Browse files
Merge pull request #286 from NASA-PDS/ldd_write_wait
Make harvest wait for LDD to be ready when it is updated.
2 parents 36f7b53 + 4e3ee64 commit 5286064

8 files changed

Lines changed: 133 additions & 7 deletions

File tree

src/main/java/gov/nasa/pds/registry/common/Request.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public interface Mapping { // _mapping
4141
}
4242
public interface MGet extends Get { // _mget
4343
public MGet setIds (Collection<String> ids);
44+
public MGet setRefresh (boolean refresh);
4445
}
4546
public interface Search { // _search
4647
public Search all(String sortField, int size, String searchAfter);
@@ -52,6 +53,7 @@ public interface Search { // _search
5253
public Search buildLidvidsFromTermQuery (String fieldname, String value);
5354
public Search buildListFields(String dataType);
5455
public Search buildListLdds (String namespace);
56+
public Search buildListLddsNoCache (String namespace);
5557
public Search buildTermQuery (String fieldname, String value);
5658
public Search buildTermQueryWithoutTermQuery (String yesFieldname, String yesValue, String noFieldname, String noValue);
5759
public Search buildTheseIds(Collection<String> lids);

src/main/java/gov/nasa/pds/registry/common/connection/aws/MGetImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ public MGet setIds(Collection<String> ids) {
4444
this.craftsman.ids(new ArrayList<String>(ids));
4545
return this;
4646
}
47+
@Override
48+
public MGet setRefresh(boolean refresh) {
49+
this.craftsman.refresh(refresh);
50+
return this;
51+
}
4752
}

src/main/java/gov/nasa/pds/registry/common/connection/aws/SearchImpl.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.ArrayList;
44
import java.util.Arrays;
55
import java.util.Collection;
6+
67
import org.opensearch.client.opensearch._types.FieldSort;
78
import org.opensearch.client.opensearch._types.FieldValue;
89
import org.opensearch.client.opensearch._types.SortOptions;
@@ -26,6 +27,7 @@
2627

2728
class SearchImpl implements Search {
2829
final SearchRequest.Builder craftsman = new SearchRequest.Builder();
30+
2931
private void buildIds (Collection<String> lids, boolean alt) {
3032
SourceConfig.Builder journeyman = new SourceConfig.Builder();
3133
if (alt) {
@@ -88,6 +90,20 @@ public Search buildListLdds(String namespace) {
8890
this.craftsman.source(new SourceConfig.Builder().filter(new SourceFilter.Builder().includes("date", "attr_name").build()).build());
8991
return this;
9092
}
93+
94+
@Override
95+
public Search buildListLddsNoCache(String namespace) {
96+
BoolQuery.Builder journeyman = new BoolQuery.Builder()
97+
.must(this.matchQuery("class_ns", "registry").build(),
98+
this.matchQuery("class_name", "LDD_Info").build(),
99+
this.matchQuery("attr_ns", namespace).build());
100+
this.craftsman.query(new Query.Builder().bool(journeyman.build()).build());
101+
this.craftsman.requestCache(false);
102+
this.craftsman.size(1000); // have no idea why hardcoded but it is (.es.JsonHelper:265
103+
this.craftsman.source(new SourceConfig.Builder().filter(new SourceFilter.Builder().includes("date", "attr_name").build()).build());
104+
return this;
105+
}
106+
91107
@Override
92108
public Search buildTheseIds(Collection<String> lids) {
93109
this.buildIds(lids, false);

src/main/java/gov/nasa/pds/registry/common/connection/es/GetImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public MGet setIds(Collection<String> ids) {
4343
return this;
4444
}
4545
@Override
46+
public MGet setRefresh(boolean refresh) {
47+
// no-op: SDK1 ES client does not use this path for AOSS eventual consistency
48+
return this;
49+
}
50+
@Override
4651
public Get setIndex(String index) {
4752
this.setIndex(index);
4853
return this;

src/main/java/gov/nasa/pds/registry/common/connection/es/SearchImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ public Search buildListLdds(String namespace) {
2828
this.json = JsonHelper.buildListLddsRequest(namespace);
2929
return this;
3030
}
31+
32+
@Override
33+
public Search buildListLddsNoCache(String namespace) {
34+
// not needed since the cache is now disabled in the data dictionary index, but we keep the method for backward compatibility
35+
this.json = JsonHelper.buildListLddsRequest(namespace);
36+
return this;
37+
}
38+
3139
@Override
3240
public Search buildTheseIds(Collection<String> lids) {
3341
this.json = JsonHelper.buildSearchIdsRequest(lids, lids.size(), true);

src/main/java/gov/nasa/pds/registry/common/es/dao/DataLoader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ private int emptyQueue(LinkedHashMap<String, String> todo, Set<String> errorLidv
268268
bulk.add(item.getKey(), item.getValue());
269269
}
270270
Response.Bulk response = this.conFactory.createRestClient().performRequest(bulk);
271+
271272
failed += processErrors(response, errorLidvids, todo, retry);
272273
retry++;
273274

src/main/java/gov/nasa/pds/registry/common/es/dao/dd/DataDictionaryDao.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import gov.nasa.pds.registry.common.util.Tuple;
1212

1313

14-
/**
14+
/**
1515
* Data dictionary DAO (Data Access Object). This class provides methods to read and update data
1616
* dictionary.
1717
*
@@ -52,6 +52,24 @@ public LddVersions getLddInfo(String namespace)
5252
return client.performRequest(req).lddInfo();
5353
}
5454

55+
/**
56+
* Get LDD date from data dictionary index in Elasticsearch. Force skip the OpenSearch cache.
57+
*
58+
* @param namespace LDD namespace, e.g., "pds", "geom", etc.
59+
* @return ISO instant class representing LDD date.
60+
* @throws IOException
61+
* @throws ResponseException
62+
* @throws UnsupportedOperationException
63+
* @throws Exception an exception
64+
*/
65+
public LddVersions getLddInfoNoCache(String namespace)
66+
throws UnsupportedOperationException, IOException {
67+
Request.Search req =
68+
client.createSearchRequest().buildListLddsNoCache(namespace).setIndex(indexName + "-dd");
69+
return client.performRequest(req).lddInfo();
70+
}
71+
72+
5573

5674
/**
5775
* List registered LDDs
@@ -110,5 +128,19 @@ public List<Tuple> getDataTypes(Collection<String> ids)
110128
return this.client.performRequest(req).dataTypes();
111129
}
112130

113-
}
131+
/**
132+
* Same as getDataTypes but forces a shard refresh before the mget. Use only in targeted wait
133+
* loops after bulk loading an LDD — do not use in normal query paths.
134+
*/
135+
public List<Tuple> getDataTypesWithRefresh(Collection<String> ids)
136+
throws IOException, DataTypeNotFoundException {
137+
if (ids == null || ids.isEmpty())
138+
return null;
139+
Request.MGet mgetReq = client.createMGetRequest();
140+
mgetReq.setRefresh(true);
141+
Request.Get req = mgetReq.setIds(ids).includeField("es_data_type")
142+
.setIndex(this.indexName + "-dd");
143+
return this.client.performRequest(req).dataTypes();
144+
}
114145

146+
}

src/main/java/gov/nasa/pds/registry/common/es/service/JsonLddLoader.java

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.File;
44
import java.time.Instant;
5+
import java.util.Collections;
56
import java.util.Map;
67
import java.util.TreeMap;
78

@@ -16,6 +17,7 @@
1617
import gov.nasa.pds.registry.common.dd.parser.DDAttribute;
1718
import gov.nasa.pds.registry.common.es.dao.DataLoader;
1819
import gov.nasa.pds.registry.common.es.dao.dd.DataDictionaryDao;
20+
import gov.nasa.pds.registry.common.es.dao.dd.DataTypeNotFoundException;
1921
import gov.nasa.pds.registry.common.es.dao.dd.LddVersions;
2022

2123

@@ -88,6 +90,10 @@ public void load(File lddFile, String lddFileName, String namespace) throws Exce
8890
namespace = LddUtils.getLddNamespace(lddFile);
8991
}
9092

93+
// get date of the current LDD for this namespace in the registry. If there is no LDD for this namespace, use epoch date.
94+
// TODO add the test of overwrite yes/no, remove it from where it is not >?
95+
96+
9197
// Get information about LDDs already loaded into the registry (for this namespace)
9298
LddVersions info = dao.getLddInfo(namespace);
9399
if (info.files.contains(lddFileName)) {
@@ -118,8 +124,42 @@ public void loadOnly(File lddFile, String lddFileName, String namespace, Instant
118124
log.info("Creating temporary ES data file " + tempEsDataFile.getAbsolutePath());
119125

120126
try {
121-
createEsDataFile(lddFile, lddFileName, namespace, tempEsDataFile, lastDate);
127+
String firstFieldId = createEsDataFile(lddFile, lddFileName, namespace, tempEsDataFile, lastDate);
122128
loader.loadFile(tempEsDataFile);
129+
130+
// Wait until the LDD_Info sentinel is visible (search with requestCache=false).
131+
// This confirms the bulk load completed on the server side.
132+
LddVersions info = dao.getLddInfoNoCache(namespace);
133+
int nAttempts = 0;
134+
int maxAttempts = 30;
135+
while (info.isEmpty() && nAttempts < maxAttempts) {
136+
Thread.sleep(1000);
137+
log.info("Waiting for the new LDD {} to be indexed...", namespace);
138+
info = dao.getLddInfoNoCache(namespace);
139+
}
140+
141+
if (info.isEmpty()) {
142+
log.warn("The new LDD {} is not indexed after {} seconds. It may be indexed later, but there may be a delay in loading other LDDs for this namespace.", namespace, maxAttempts);
143+
} else {
144+
log.info("The new LDD {} is indexed with date {}. It may take some time for it to be visible via mget.", namespace, info.lastDate);
145+
146+
// On AWS OpenSearch Serverless (AOSS), mget and search use different visibility paths.
147+
// Wait until a specific field document is also visible via mget with refresh=true,
148+
// so that getDataTypes() calls immediately following this load will succeed.
149+
if (firstFieldId != null) {
150+
boolean fieldVisible = false;
151+
while (!fieldVisible) {
152+
try {
153+
dao.getDataTypesWithRefresh(Collections.singletonList(firstFieldId));
154+
fieldVisible = true;
155+
} catch (DataTypeNotFoundException e) {
156+
Thread.sleep(1000);
157+
log.info("Waiting for field {} of namespace {} to be visible via mget...", firstFieldId, namespace);
158+
}
159+
}
160+
}
161+
log.info("Visibility of namespace " + namespace + "has been fully validated.");
162+
}
123163
} finally {
124164
// Delete temporary file
125165
tempEsDataFile.delete();
@@ -128,15 +168,30 @@ public void loadOnly(File lddFile, String lddFileName, String namespace, Instant
128168

129169

130170
private static class CaaCallback implements ClassAttrAssociationParser.Callback {
131-
private LddEsJsonWriter writer;
171+
private final LddEsJsonWriter writer;
172+
private final String namespace;
173+
private final Map<String, DDAttribute> ddAttrCache;
174+
private String firstFieldId;
132175

133-
public CaaCallback(LddEsJsonWriter writer) {
176+
public CaaCallback(LddEsJsonWriter writer, String namespace, Map<String, DDAttribute> ddAttrCache) {
134177
this.writer = writer;
178+
this.namespace = namespace;
179+
this.ddAttrCache = ddAttrCache;
135180
}
136181

137182
@Override
138183
public void onAssociation(String classNs, String className, String attrId) throws Exception {
139184
writer.writeFieldDefinition(classNs, className, attrId);
185+
if (firstFieldId == null && namespace.equals(classNs)) {
186+
DDAttribute attr = ddAttrCache.get(attrId);
187+
if (attr != null) {
188+
firstFieldId = classNs + ":" + className + "/" + attr.attrNs + ":" + attr.attrName;
189+
}
190+
}
191+
}
192+
193+
public String getFirstFieldId() {
194+
return firstFieldId;
140195
}
141196
}
142197

@@ -149,7 +204,7 @@ public void onAssociation(String classNs, String className, String attrId) throw
149204
* @param tempEsFile Write to this Elasticsearch file
150205
* @throws Exception an exception
151206
*/
152-
private void createEsDataFile(File lddFile, String lddFileName, String namespace, File tempEsFile,
207+
private String createEsDataFile(File lddFile, String lddFileName, String namespace, File tempEsFile,
153208
Instant lastDate) throws Exception {
154209
// Parse and cache LDD attributes
155210
Map<String, DDAttribute> ddAttrCache = new TreeMap<>();
@@ -169,13 +224,15 @@ private void createEsDataFile(File lddFile, String lddFileName, String namespace
169224
writer.setNamespaceFilter(namespace);
170225

171226
// Parse class attribute associations and write to ES data file
172-
CaaCallback ccb = new CaaCallback(writer);
227+
CaaCallback ccb = new CaaCallback(writer, namespace, ddAttrCache);
173228
ClassAttrAssociationParser caaParser = new ClassAttrAssociationParser(lddFile, ccb);
174229
caaParser.parse();
175230

176231
// Write data dictionary version and date
177232
writer.writeLddInfo(namespace, lddFileName, attrParser.getImVersion(),
178233
attrParser.getLddVersion(), attrParser.getLddDate());
234+
235+
return ccb.getFirstFieldId();
179236
} finally {
180237
writer.close();
181238
}

0 commit comments

Comments
 (0)