Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Assignment
- The goal of the assignment is to build a user profile management service. It will have methods to create, change and get the user profile.
- The service uses Redis as main database to keep latest data and hadoop to store all activity. External databases will be mocked so development would need less time for setup.
- NB! The solution must not be publicly available in code repositories like GitHub etc before June 15, 2014.
- The main points of implementation:
- · External API and logic behind it. API is descibed below. Also the data model.
- · Idempotency and change conflicts. Explained below.
- · Background process to push events to a separate database and delete them from redis.
- · Internal event queue/cache.
- Restrictions and Help
- You will be provided a sample project, that has an example service „hello world“ implemented. In this example also redis is mocked and interface is provided to communicate with hadoop datastore.
- Usage of external libraries is restricted. Only existing libraries/dependencies can be used. Everything else should be implemented in the project.
- Implement as much as you can during the given timeframe.
- API description
- · /user/create
- o Request parameters:
- § actor=<string>, mandatory
- § username=<string>, mandatory
- § changeid=<string>, mandatory
- § firstname=<string>
- § lastname=<string>
- § birthdate=<YYYY.MM.DD>
- § currency=<string>
- § language=<string>
- § email=<string>, mandatory
- o Response parameters:
- § userid=<string>, mandatory on success
- § errorText=<string>, optional
- o Response errors:
- § 200 – success (or retry)
- § 400 – invalid input
- § 409 – Duplicate username
- § 500 – system error
- · /user/change
- o Request parameters:
- § actor=<string>, mandatory
- § userid=<string>, mandatory
- § version=<integer>, mandatory – what version of user profile was used to make the decision of changing it.
- § changeid=<string>, mandatory
- § username=<string>
- § firstname=<string>
- § lastname=<string>
- § birthdate=<YYYY.MM.DD>
- § currency=<string>
- § language=<string>
- § email=<string>
- o Response parameters:
- § errorText=<string>, optional
- § version=<integer>, current version
- o Response errors
- § 200 – success (or retry)
- § 400 – invalid input
- § 404 – user profile not found
- § 409 – change conflict or duplicate username
- § 500 – system error
- · /user/get
- o Request parameters:
- § actor=<string>, mandatory
- § userid=<string>, username or userid mandatory
- § username=<string>, username or userid mandatory
- o Response parameters
- § errorText=<string>, optional
- § userid=<string>, mandatory on success
- § username=<string>, mandatory on success
- § firstname=<string>
- § lastname=<string>
- § birthdate=<YYYY.MM.DD>
- § currency=<string>
- § language=<string>
- § email=<string>, mandatory on success
- § signuptime=<YYYY.MM.DD HH:mm:ss.SSS>, mandatory on success
- § version=<integer>, mandatory on success
- § changetime=<YYYY.MM.DD HH:mm:ss.SSS>, mandatory on success
- o Response errors
- § 200 – success
- § 400 – invalid input
- § 404 – user profile not found
- § 500 – system error
- Data structures
- · User profile object – Contains latest user profile info.
- o TTL – indefinite
- o Key – USER:PROFILE:<userid>
- o Data (in json format)
- § userid
- § username
- § firstname
- § lastname
- § birthdate
- § currency
- § language
- § email
- § signuptime
- § version
- § changetime
- · User profile change versions – Used during change process to avoid data races.
- o TTL – 1d
- o Key – USER:PVERSIONS:<userid>:<version>
- o Data (in json format)
- § Same as User object.
- · Profile change changeids – Used during create and change to do idempotency checks.
- o TTL – 1d
- o Key – USER:PCHANGEID:<userid>:<changeid>
- o Data (in json format)
- § version
- · Usernames – Mapping of usernames to userids. Needed to make initial resolution, when username is used for reference.
- o TTL – indefinite
- o Key – USER:USERNAMES:username
- o Data (in json format)
- § userid
- · Events – temporary event history backup, before sending to hadoop or some other storage.
- o TTL – indefinite
- o Key – EVENTS:eventid
- o Data (in json format)
- § id – event id.
- § name – create, change or get. Event name.
- § userid – reference to user profile
- § actor – reference to who is causing the event
- § ip – address of the request
- § timestamp – time of the event in milliseconds
- § request – json string of request
- Concepts explained
- · Itempotency – The internet is not bulletproof. There will be networking issues temporarily somewhere. Good system has a way to deal with retrys.
- · Entity versioning – Different parts of the system might show or process the same entity (admin, user facing client, other services). These parts of the system rarely change the same parameters, but do change at the same time. Versioning allows to identify conflicts or get around it, if different parts of the system change different parameters at the same time.
- o Example:
- § User opens its profile page. Profile data version 3 is shown.
- § User is slow. It does not make an action within 5min.
- § Meanwhile a system process changes user segmentation group. Version is updated to 4.
- § Now user does the update (firstname typo). Version=3 is sent in request.
- § System can compare version 3 and new change request. It identifies, that firstname is changed. Firstname was not changed with version 4 update, so there are no conflicts.
- · Optimistic locking – Redis does not support transactions as such. Instead optimistic locking is used.
- o How to use: http://redis.io/topics/transactions
- o Example:
- § WATCH key – start optimistic lock
- § GET key – get value
- § Processing in server...
- § MULTI – redis transaction (means just, that commands are executed in one batch)
- § SET key ... – change data
- § EXEC – end transaction (actual execution of commands)
- o Example java:
- IRedisClient jedis = getRedisManager().getJedis();
- try {
- // start transaction and get profile
- jedis.watch(profileKey);
- String profilejson = jedis.get(key);
- ...
- // start batch
- IRedisClient t = jedis.multi();
- t.set(profileKey, newprofilejson);
- t.exec();
- } finally {
- getRedisManager().returnJedis(jedis);
- }
- Examples
- Redis communication (in HelloWorldService):
- IRedisClient jedis = RedisManager.getInstance().getJedis();
- try {
- jedis.set(key, value);
- } catch (Exception e) {
- throw new RuntimeException("Redis issue!", e);
- } finally {
- RedisManager.getInstance().returnJedis(jedis);
- }
- POJO to json conversion (in HelloWorldService):
- // pojo to json
- String json = mapper.writeValueAsString(result);
- System.out.println( "to json> " + json.getClass() + "::" + json );
- // json to pojo
- HelloWorldBuilder builder = mapper.readValue(json, HelloWorldBuilder.class);
- System.out.println( "from json> " + builder.build() );
- API usage scenarios:
- Create user missing parameters
- http://localhost:9090/user/create?actor=me&email=someone@meil.me&changeid=34j53h4
- {"errortext":"Invalid input: Parameter username is mandatory."}
- Create user
- http://localhost:9090/user/create?actor=me&email=someone@meil.me&changeid=3243243kj4dsfjdsvf32kj&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b"}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"2","username":"someone1","firstname":"newname",
- "lastname":null,"birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 14:55:36.853","signupdate":"2015.04.110 14:55:03.090"}
- Change user firstname
- http://localhost:9090/user/change?actor=me&username=someone1&changeid=1000&firstname=newname&version=1
- {"version":"2"}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"2","username":"someone1","firstname":"newname",
- "lastname":null,"birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 14:55:36.853","signupdate":"2015.04.110 14:55:03.090"}
- Create retry
- http://localhost:9090/user/create?actor=me&email=someone@meil.me&changeid=3243243kj4dsfjdsvf32kj&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b "}
- Create conflict
- http://localhost:9090/user/create?actor=me&email=someone@meil.me&changeid=k45n3jk4b5&username=someone1
- {"errortext":"Username already in use."}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"2","username":"someone1","firstname":"newname",
- "lastname":null,"birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 14:55:36.853","signupdate":"2015.04.110 14:55:03.090"}
- Invalid create retry
- http://localhost:9090/user/create?actor=me&email=someone@meil.me&changeid=3243243kj4dsfjdsvf32kj&username=someone1&lastname=tamm
- {"errortext":"lastname retry conflict. Was null, now tamm."}
- Non-conflicting change
- http://localhost:9090/user/change?actor=me&username=someone1&changeid=1001&lastname=newname&version=1
- {"version":"3"}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"3","username":"someone1","firstname":"newname",
- "lastname":"newname","birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 15:10:11.417","signupdate":"2015.04.110 15:09:59.308"}
- Conflicting change
- http://localhost:9090/user/change?actor=me&username=someone1&changeid=1002&firstname=tester&version=1
- {"errortext":"Firstname change conflict."}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"3","username":"someone1","firstname":"newname",
- "lastname":"newname","birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 15:10:11.417","signupdate":"2015.04.110 15:09:59.308"}
- Change retry
- http://localhost:9090/user/change?actor=me&username=someone1&changeid=1001&lastname=newname&version=1
- {"version":"3"}
- User does not exist
- http://localhost:9090/user/get?actor=me&username=someone2
- {"errortext":"Profile not found (username): someone2"}
- User does not exist
- http://localhost:9090/user/get?actor=me&userid=someone2
- {"errortext":"Profile not found (userid): someone2"}
- Change username
- http://localhost:9090/user/change?actor=me&userid=8b8963a5-7076-4ba2-acbe-156675c0b93b&changeid=1004&username=tester1&version=3
- {"version":"4"}
- Get user
- http://localhost:9090/user/get?actor=me&username=someone1
- {"errortext":"User profile not found by username: someone1"}
- Get user
- http://localhost:9090/user/get?actor=me&username=tester1
- {"userid":"8b8963a5-7076-4ba2-acbe-156675c0b93b","version":"4","username":"tester1","firstname":"newname",
- "lastname":"newname","birthdate":null,"currency":null,"language":null,"email":"someone@meil.me",
- "changetime":"2015.04.110 15:23:37.510","signupdate":"2015.04.110 15:23:22.072"}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement