-
Notifications
You must be signed in to change notification settings - Fork 214
/
SearchUpdateMapper.java
139 lines (120 loc) · 5.44 KB
/
SearchUpdateMapper.java
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.thingsearch.service.persistence.write.streaming;
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import org.eclipse.ditto.internal.utils.extension.DittoExtensionPoint;
import org.eclipse.ditto.internal.utils.extension.DittoExtensionIds;
import org.eclipse.ditto.internal.utils.metrics.instruments.timer.StartedTimer;
import org.eclipse.ditto.internal.utils.persistence.mongo.DittoMongoClient;
import org.eclipse.ditto.thingsearch.service.persistence.write.model.AbstractWriteModel;
import org.eclipse.ditto.thingsearch.service.starter.actors.MongoClientExtension;
import org.eclipse.ditto.thingsearch.service.updater.actors.MongoWriteModel;
import org.slf4j.Logger;
import com.typesafe.config.Config;
import org.apache.pekko.NotUsed;
import org.apache.pekko.actor.ActorSystem;
import org.apache.pekko.stream.javadsl.Source;
/**
* Search Update Mapper to be loaded by reflection.
* Can be used as an extension point to use custom map search updates.
* Implementations MUST have a public constructor taking an actorSystem as argument.
*
* @since 2.1.0
*/
public abstract class SearchUpdateMapper implements DittoExtensionPoint {
protected final ActorSystem actorSystem;
protected final int maxWireVersion;
protected SearchUpdateMapper(final ActorSystem actorSystem, final Config config) {
this(actorSystem, getMaxWireVersion(actorSystem));
}
protected SearchUpdateMapper(final ActorSystem actorSystem, final Integer maxWireVersion) {
this.actorSystem = actorSystem;
this.maxWireVersion = maxWireVersion;
}
private static int getMaxWireVersion(final ActorSystem system) {
final DittoMongoClient client = MongoClientExtension.get(system).getUpdaterClient();
return client.getMaxWireVersion();
}
/**
* Gets a write model of the search update and processes it.
*
* @param writeModel the write model.
* @param lastWriteModel the last write model to compute incremental update from.
* @return Ditto write model together with its processed MongoDB write model.
*/
public abstract Source<MongoWriteModel, NotUsed> processWriteModel(AbstractWriteModel writeModel,
final AbstractWriteModel lastWriteModel);
/**
* Load a {@code SearchUpdateListener} dynamically according to the search configuration.
*
* @param actorSystem The actor system in which to load the listener.
* @param config the configuration for this extension.
* @return The listener.
*/
public static SearchUpdateMapper get(final ActorSystem actorSystem, final Config config) {
checkNotNull(actorSystem, "actorSystem");
checkNotNull(config, "config");
final var extensionIdConfig = ExtensionId.computeConfig(config);
return DittoExtensionIds.get(actorSystem)
.computeIfAbsent(extensionIdConfig, ExtensionId::new)
.get(actorSystem);
}
/**
* Convert a write model to an incremental update model.
*
* @param model the write model.
* @param logger the logger.
* @return a singleton list of write model together with its update document, or an empty list if there is no
* change.
*/
protected Source<MongoWriteModel, NotUsed>
toIncrementalMongo(final AbstractWriteModel model, final AbstractWriteModel lastWriteModel, final Logger logger) {
try {
final var mongoWriteModelOpt = model.toIncrementalMongo(lastWriteModel, maxWireVersion);
if (mongoWriteModelOpt.isEmpty()) {
logger.debug("Write model is unchanged, skipping update: <{}>", model);
model.getMetadata().sendWeakAck(null);
return Source.empty();
} else {
ConsistencyLag.startS5MongoBulkWrite(model.getMetadata());
final var result = mongoWriteModelOpt.orElseThrow();
logger.debug("MongoWriteModel={}", result);
return Source.single(result);
}
} catch (final Exception error) {
logger.error("Failed to compute write model " + model, error);
try {
model.getMetadata().getTimers().forEach(StartedTimer::stop);
} catch (final Exception e) {
// tolerate stopping stopped timers
}
return Source.empty();
}
}
/**
* ID of the actor system extension to validate the {@code SearchUpdateListener}.
*/
private static final class ExtensionId extends DittoExtensionPoint.ExtensionId<SearchUpdateMapper> {
private static final String CONFIG_KEY = "search-update-mapper";
private ExtensionId(final ExtensionIdConfig<SearchUpdateMapper> extensionIdConfig) {
super(extensionIdConfig);
}
static ExtensionIdConfig<SearchUpdateMapper> computeConfig(final Config config) {
return ExtensionIdConfig.of(SearchUpdateMapper.class, config, CONFIG_KEY);
}
@Override
protected String getConfigKey() {
return CONFIG_KEY;
}
}
}