This is an automated email from the ASF dual-hosted git repository. ningjiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-saga.git
The following commit(s) were added to refs/heads/master by this push: new 10cdd07 Implement Saga Console API's 10cdd07 is described below commit 10cdd07742497d349c087c8f641e466455b137bb Author: asifdxtreme <mohammad.asif.siddiq...@huawei.com> AuthorDate: Tue Oct 30 16:28:22 2018 +0530 Implement Saga Console API's --- .../alpha/server/TxEventEnvelopeRepository.java | 137 ++++++++++- .../console/saga/SagaTransactionsController.java | 255 +++++++++++++++++++++ .../alpha/server/console/saga/model/Stats.java | 103 +++++++++ 3 files changed, 494 insertions(+), 1 deletion(-) diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/TxEventEnvelopeRepository.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/TxEventEnvelopeRepository.java index 89808c9..ebb7610 100644 --- a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/TxEventEnvelopeRepository.java +++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/TxEventEnvelopeRepository.java @@ -28,7 +28,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; -interface TxEventEnvelopeRepository extends CrudRepository<TxEvent, Long> { +public interface TxEventEnvelopeRepository extends CrudRepository<TxEvent, Long> { List<TxEvent> findByGlobalTxId(String globalTxId); @Query("SELECT t FROM TxEvent t " @@ -124,6 +124,141 @@ interface TxEventEnvelopeRepository extends CrudRepository<TxEvent, Long> { + " AND t1.surrogateId > t.surrogateId )") List<TxEvent> findDuplicateEventsByType(String type); + List<TxEvent> findByServiceName(String serviceName); + + @Query("SELECT count(t) FROM TxEvent t" + + " WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent'))") + int findCountOfCompensatingEvents(); + + @Query("SELECT t FROM TxEvent t" + + " WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findCompensatingEvents(); + + @Query("SELECT t FROM TxEvent t" + + " WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findCompensatingEvents(Pageable pageable); + + @Query("SELECT count(t) FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent', 'TxCompensatedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent'))\n") + int findCountOfCommittedEvents(); + + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent', 'TxCompensatedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findCommittedEvents(); + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent', 'TxCompensatedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findCommittedEvents(Pageable pageable); + + @Query("SELECT count(t) FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent'))\n") + int findCountOfPendingEvents(); + + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findPendingEvents(); + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND NOT EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findPendingEvents(Pageable pageable); + + @Query("SELECT count(t) FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM " + + "TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxCompensatedEvent'))\n") + int findCountOfRollBackedEvents(); + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM " + + "TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxCompensatedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findRollBackedEvents(); + + @Query("SELECT t FROM TxEvent t " + + "WHERE t.type = 'SagaStartedEvent' " + + "AND EXISTS( SELECT t1.globalTxId " + + "FROM TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxAbortedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM " + + "TxEvent t1 WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('SagaEndedEvent')) " + + "AND EXISTS( SELECT t1.globalTxId FROM TxEvent t1 " + + "WHERE t1.globalTxId = t.globalTxId " + + "AND t1.type IN ('TxCompensatedEvent')) " + + "ORDER BY t.surrogateId DESC") + List<TxEvent> findRollBackedEvents(Pageable pageable); + + @Query("SELECT count(DISTINCT t.globalTxId) FROM TxEvent t") + int findTotalCountOfTransactions(); + @Transactional @Modifying(clearAutomatically = true) @Query("DELETE FROM TxEvent WHERE surrogateId = ?1 ") diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/SagaTransactionsController.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/SagaTransactionsController.java new file mode 100644 index 0000000..bafb25d --- /dev/null +++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/SagaTransactionsController.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.saga.alpha.server.console.saga; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; + +import kamon.annotation.EnableKamon; +import kamon.annotation.Trace; + +import org.apache.servicecomb.saga.alpha.core.TxEvent; +import org.apache.servicecomb.saga.alpha.server.TxEventEnvelopeRepository; +import org.apache.servicecomb.saga.alpha.server.console.saga.model.Stats; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.lang.invoke.MethodHandles; +import java.util.*; + +@EnableKamon +@Controller +@RequestMapping("/saga") + +public class SagaTransactionsController { + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final TxEventEnvelopeRepository eventRepository; + + SagaTransactionsController(TxEventEnvelopeRepository eventRepository) { + this.eventRepository = eventRepository; + } + + @Trace("getStats") + @GetMapping(value = "/stats") + ResponseEntity<Stats> getStats() { + /* + This function gives returns the count of the transactions listed below + 1. Total Transactions + 2. PENDING + 3. COMMITTED + 4. COMPENSATING + 5. ROLLBACKED + 6. FAILURERATE + + Sample Response + { + "totalTransaction": 10, + "PENDING": 3, + "COMMITTED": 1, + "COMPENSATING": 2, + "ROLLBACKED": 4, + "FAILURERATE": 60, + "updatedAt": 1540450937 + } + */ + Stats oStats = new Stats(eventRepository.findTotalCountOfTransactions(), eventRepository.findCountOfPendingEvents(), + eventRepository.findCountOfCommittedEvents(), eventRepository.findCountOfCompensatingEvents(), + eventRepository.findCountOfRollBackedEvents()); + return ResponseEntity.ok(oStats); + } + + + @Trace("getRecentTransactions") + @GetMapping(value = "/recent") + ResponseEntity<Collection<SagaTransactionsController.TxEventVo>> recentTransactions( + @RequestParam(name = "status") String status, @RequestParam(name = "count") int count) { + /* + This will return the list of recent transactions + Parameters : + status - Status of transaction (PENDING, COMMITTED, COMPENSATING, ROLLBACKED) + count - Number of last transactions to be returned (max: 15, default: 5) + + Sample Response + [ + { + "surrogateId": 6, + "serviceName": "booking", + "instanceId": "booking-172.23.0.7", + "creationTime": 1540823579335, + "globalTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "localTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "parentTxId": null, + "type": "SagaEndedEvent", + "compensationMethod": "", + "expiryTime": 253402214400000, + "retryMethod": "", + "retries": 0, + "payloads": "AQE=" + } + ] + */ + Iterable<TxEvent> events; + + List<SagaTransactionsController.TxEventVo> eventVos = new LinkedList<>(); + switch (status) { + case "PENDING": + events = eventRepository.findPendingEvents(new PageRequest(0, count)); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "COMMITTED": + events = eventRepository.findCommittedEvents(new PageRequest(0, count)); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "COMPENSATING": + events = eventRepository.findCompensatingEvents(new PageRequest(0, count)); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "ROLLBACKED": + events = eventRepository.findRollBackedEvents(new PageRequest(0, count)); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + default: + LOG.info("Unknown Status"); + } + return ResponseEntity.ok(eventVos); + } + + @Trace("getTransactions") + @GetMapping(value = "/transactions") + ResponseEntity<Collection<SagaTransactionsController.TxEventVo>> getTransactions( + @RequestParam(name = "status") String status) { + /* + This will return the list of transactions with pagination enabled. + Parameters : + status - Status of transaction (ALL, PENDING, COMMITTED, COMPENSATING, ROLLBACKED) + pagination - is pagination enabled + countPerPage - List of elements per page + pageNumber - Current Page to be sent + + Sample Response + [ + { + "surrogateId": 6, + "serviceName": "booking", + "instanceId": "booking-172.23.0.7", + "creationTime": 1540823579335, + "globalTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "localTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "parentTxId": null, + "type": "SagaEndedEvent", + "compensationMethod": "", + "expiryTime": 253402214400000, + "retryMethod": "", + "retries": 0, + "payloads": "AQE=" + } + ] + */ + //TODO Pagination + + Iterable<TxEvent> events; + + List<SagaTransactionsController.TxEventVo> eventVos = new LinkedList<>(); + + switch (status) { + case "PENDING": + events = eventRepository.findPendingEvents(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "COMMITTED": + events = eventRepository.findCommittedEvents(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "COMPENSATING": + events = eventRepository.findCompensatingEvents(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "ROLLBACKED": + events = eventRepository.findRollBackedEvents(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + case "ALL": + events = eventRepository.findAll(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + break; + default: + LOG.info("Unknown Status"); + } + + return ResponseEntity.ok(eventVos); + } + + @Trace("findTransactions") + @GetMapping(value = "/findTransactions") + ResponseEntity<Collection<SagaTransactionsController.TxEventVo>> findTransactions( + @RequestParam(required = false, name = "globalTxID") Object globalTxID, + @RequestParam(required = false, name = "microServiceName") String microServiceName) { + /* + This will return all the list of sub-transactions for a particular Global ID or MicroService. + Parameter : + globalID : GlobalID of the transactions + microserviceName : Name of the Microservice + Sample Response + [ + { + "surrogateId": 6, + "serviceName": "booking", + "instanceId": "booking-172.23.0.7", + "creationTime": 1540823579335, + "globalTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "localTxId": "daa74121-9e00-4567-96b7-dc9fd163060b", + "parentTxId": null, + "type": "SagaEndedEvent", + "compensationMethod": "", + "expiryTime": 253402214400000, + "retryMethod": "", + "retries": 0, + "payloads": "AQE=" + } + ] + */ + + Iterable<TxEvent> events; + if (globalTxID != null) { + events = eventRepository.findByGlobalTxId(globalTxID.toString()); + } else if (microServiceName != "") { + events = eventRepository.findByServiceName(microServiceName); + } else { + events = null; + } + + Collection<TxEventVo> eventVos = new LinkedList<>(); + events.forEach(event -> eventVos.add(new SagaTransactionsController.TxEventVo(event))); + + return ResponseEntity.ok(eventVos); + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + private static class TxEventVo extends TxEvent { + private TxEventVo(TxEvent event) { + super(event); + } + } +} diff --git a/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/model/Stats.java b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/model/Stats.java new file mode 100644 index 0000000..befaa03 --- /dev/null +++ b/alpha/alpha-server/src/main/java/org/apache/servicecomb/saga/alpha/server/console/saga/model/Stats.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.saga.alpha.server.console.saga.model; + +import java.util.Date; + +public class Stats { + + public int getTotalTransactions() { + return totalTransactions; + } + + public void setTotalTransactions(int totalTransactions) { + this.totalTransactions = totalTransactions; + } + + private int totalTransactions; + + public int getPendingTransactions() { + return pendingTransactions; + } + + public void setPendingTransactions(int pendingTransactions) { + this.pendingTransactions = pendingTransactions; + } + + public int getCommittedTransactions() { + return committedTransactions; + } + + public void setCommittedTransactions(int committedTransactions) { + this.committedTransactions = committedTransactions; + } + + public int getCompensatingTransactions() { + return compensatingTransactions; + } + + public void setCompensatingTransactions(int compensatingTransactions) { + this.compensatingTransactions = compensatingTransactions; + } + + public int getRollbackTransactions() { + return rollbackTransactions; + } + + public void setRollbackTransactions(int rollbackTransactions) { + this.rollbackTransactions = rollbackTransactions; + } + + private int pendingTransactions; + + private int committedTransactions; + + private int compensatingTransactions; + + private int rollbackTransactions; + + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + private Date updatedAt; + + public int getFailureRate() { + return failureRate; + } + + public void setFailureRate(int failureRate) { + this.failureRate = failureRate; + } + + private int failureRate; + + public Stats(int totalTransactions, int pendingTransactions, int committedTransactions, int compensatingTransactions, + int rollbackTransactions) { + setTotalTransactions(totalTransactions); + setPendingTransactions(pendingTransactions); + setCommittedTransactions(committedTransactions); + setCompensatingTransactions(compensatingTransactions); + setRollbackTransactions(rollbackTransactions); + setFailureRate(rollbackTransactions + compensatingTransactions / totalTransactions); + setUpdatedAt(new Date()); + } +}