问题:我们在生产中使用了几个 lambdas 和 dynamodb 表,当发布新版本的代码时,我们有时会剥离属性或将属性添加到表类(使用 com.amazonaws.services.dynamodbv2.datamodeling 的 Java 代码)高级 API。当我们部署新版本的代码并查询表时,如果现有项目不存在新属性,或者我们从代码中删除属性。它破坏了代码,因为我们的 Item 对象与生产数据不一致。
我们希望通过添加具有默认值的额外属性或删除现有项目的属性来避免处理 prod 中的数据。在我们部署新版本之前,出于各种有关竞争条件和一致性的原因。如果我们在代码级别处理它会更可取,如果属性不存在自动添加默认值。或者让代码忽略项目/表格类中未定义的属性。这可以使用高级java sdk api吗?
我们提出的另一个解决方案是创建一个服务,该服务提供 delta(代码项对象和 prod 中的数据之间的变化),由一个 pretraffic lambda 执行,该 lambda 在部署新版本的 lambda 时处理数据。然而,我们想避免这种情况。
package com.ourcompany.module.dynamodb.items;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute;
import lombok.Data;
@Data
@DynamoDBTable(tableName = "Boxes")
public class BoxItem {
@DynamoDBHashKey(attributeName = "boxID")
private String channelID;
#This is the field we added, the previous version did not have this field, in prod we have many items without this attribute
@DynamoDBAttribute(attributeName = "lastTimeAccess")
private String lastTimeAccess;
@DynamoDBAttribute(attributeName = "initTime")
private String initTime;
@DynamoDBAttribute(attributeName = "boxIDhash")
private String streamBoxIDHash;
@DynamoDBAttribute(attributeName = "CFD")
private String cfd;
@DynamoDBAttribute(attributeName = "originDomain")
private String originDomain;
@DynamoDBAttribute(attributeName = "lIP")
private String lIP;
@DynamoDBAttribute(attributeName = "pDomain")
private String pDomain;
上面是我们更改的项目,添加了属性。
package com.ourcompany.shared.module.repository.dynamob;
import ...
public class DynamoDbRepository<Item, Key> {
private final DynamoDBMapper mapper;
private static final Logger logger = LogManager.getLogger(DynamoDbRepository.class);
@Inject
public DynamoDbRepository() {
val client = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.US_EAST_1) // TODO: hardcoded now
.withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder()))
.build();
DynamoDBMapperConfig dynamoDBMapperConfig = new DynamoDBMapperConfig.Builder()
.withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES)
.withTableNameResolver(new DynamoDBTableNameResolver())
.build();
mapper = new DynamoDBMapper(client, dynamoDBMapperConfig);
}
/*
* Many accessor methods are listed here below is the one where we have issue,
*/
public List<Item> findBy(Map<String, Condition> filter, final Class<Item> clazz) throws Exception {
try {
logger.trace("DynamoDbRepository findBy(filter, class)");
val scanExpression = new DynamoDBScanExpression().withScanFilter(filter).withConsistentRead(true);
PaginatedScanList<Item> ls = mapper.scan(clazz, scanExpression);
ls.loadAllResults();
return ls;
} catch (Exception ex) {
logger.trace(ex.getMessage());
throw handleException(ex);
}
}
以上是我们的 Dynamob DB 映射器类,但只包含有问题的方法。我们能够通过记录跟踪到 logger.trace("DynamoDbRepository findBy(filter, class)"); 行,并且我们知道问题出现在映射器中。但是它不会吐出异常,所以我们看不到实际的错误。我们必须通过从 prod 中的表中清除所有数据来解决该问题,然后让新版本的代码使用属性重新填充条目并且代码正常工作。