Monday, 26 December 2016

Amazon gift cards (how Amazon can take your money and give nothing in return)

Customer service? I think not

Final update (26/12/16 16:30): Following a Tweet from me Amazon’s Social Media Team got in touch and resolved the issue, finally. Well done them and thanks to the lady who sorted it out for me. Case closed. I’ve left the blog post below for information.

My son received 2 gift cards from a good friend of our this Christmas. Both the cards were purchased at the same time direct from Amazon and were delivered direct to us. Both cards arrived at the same time in the same package. No 3rd party retailers were involved.

My son was able to redeem the first card but the second one – with a value of £20 - simply wouldn’t work.

So, I contact Amazon customer services using their online email facility but the results are shockingly poor and have left me feeling like I’m being taken for a ride.

So far they have strung me along with an exchange of 11 emails and one long phone call. I’ve included the whole thing below because of it’s absurdity. In short they start by saying the card was not authorised by the ‘retailer’ and that I should contact them. I point out Amazon was the retailer – no 3rd parties involved. They respond by asking for a PDF of the card which I send. They then ask me to provide the following gem of information:

“If you're able to see the any 3 consecutive digits of the claim code, other than the first 2 or last 4, please reply to this e-mail with these numbers along with the 16 digit card number located on the back of the card.”

Remember, I’d just send them a PDF of the whole card. You have to assume I’m being strung along at this point.

To rub salt in my wounds Amazon includes the following message at the bottom of each email:

“Your feedback is helping us build Earth's Most Customer-Centric Company.”

That starts to look like a bit of a joke. Anyway, so far no I’ve not been able to resolve the issue. To cut a long story short, Amazon has taken money from a friend of mine and provided nothing in return. What a great way to make money!

The whole email trail follows if you are interested (I’ve omitted the card number for obvious reasons).

The email trail

25/12/16 13:25:20 from me

A gift card given to my son does not work. It's for £20 and has number ****-******-*****. Please advise.

25/12/16 15:34 from Amazon Customer Services

Hello,

I'm sorry to hear you've had trouble using the Gift Card you received for your son and I’ll be happy to help you today.

I’ve checked your account, and can see that according to our records the Gift Card wasn't activated by the retailer it was purchased from.

Unfortunately, we can't activate this Gift Card for you as it was not created on our system.

The best action to take in this situation is to bring the Gift Card back to the point of purchase with your receipt, where the retailer can reissue a new card for you to use. If you no longer have the receipt, please contact the shop where the card was purchased to resolve this.

Your patience and understanding is greatly appreciated.​

If you need any further information or assistance, please let us know by replying to this e-mail so that we'll be happy to help you further.
We look forward to seeing you again soon.

25/12/16 17:43 from me

Sorry, but I think you are mistaken.

Two gift cards arrived at the same time and were purchased by a friend direct from amazon.co.uk, not a 3rd party. The 2 cards arrived in the same package and came direct from you. One card worked, the second did not. I believe the mistake is at your end.

Andrew French

25/12/16 18:12 from Amazon Customer Services

Hello,

I'm sorry to hear that you were unable to use one of your Gift Cards.

We will be happy to take the necessary action.

If you have received the Physical Gift card, I request you to attach the picture of the front and back side of Gift Card.

Please attach the picture as a PDF, JPG or PNG file.

If you have received the Email Gift card, I request you to just copy and past the entire gift card information, and send it to us.

I am sorry in making you to write back to us, but this will help us in resolving this issue for you in an efficient manner.

Thank you for you patience and understanding in this regard.

We look forward to seeing you again soon.​​​​​​​​​

25/12/16 19:08 from me

Please find the PDF attached as requested.
[I attached the PDF to the email]

25/12/16 20:54 from Amazon Customer Services

Hello Andy,

Firstly, please accept my sincere apologies for any inconvenience caused by this situation.

I understand the level of disappointment this has caused to you. If I had been in this situation, I would have felt the same.​

You have been a loyal and supportive customer with us since a long time, I highly appreciate your support with us.​​​

It's never our intention to cause inconvenience to our honest and valuable customer like you.​

Further to your email, I understand that the 2 Gift card were purchased by your friend but one Gift card is working another one is not working.

In this situation to help you further, I have checked  your friend account omitted@omitted.co.uk  and see that he/she purchased only one gift card from our direct store and the order number for the one is #***-*******-*******.

I have checked the image you have provided to us and can see that according to our records the Gift Card wasn't activated by the retailer it was purchased from.
Unfortunately, we can't activate this Gift Card for you as it was not created on our system.

The best action to take in this situation is to bring the Gift Card back to the point of purchase with your receipt, where the retailer can reissue a new card for you to use. If you no longer have the receipt, please contact the shop where the card was purchased to resolve this.

Should you require a​ny additional information or assistance, please do not hesitate to contact us.​

Once again, please let me apologies for any inconvenience this has caused. It is never our intention to cause any sort on inconvenience to our valued customers like you.​​

We look forward to seeing you again soon.

25/12/16 21/31 from me

No, you have misunderstood again.

The account you reference (omitted@omitted.co.uk) is not the purchaser of the gift cards but the recipient! The card you site as having been ‘purchased’ was not purchased at all but was redeemed. It is the one card that did work (as per email trail below). The card we are talking about here did not work.

I have not given you the purchaser’s account name because I do not have it.
What I can say with a certainty is that both cards were purchased at the same time. They both arrived together in the same package direct from Amazon, not a 3rd party. THERE IS NO 3RD PARTY RETAILER TO CONTACT. AMAZON WAS THE RETAILER OF BOTH CARDS.

I suggest you credit my son’s account (omitted@omitted.co.uk), the intended recipient of the card, with the £20 value and cancel the card itself. You can then take whatever steps are necessary to sort out the confusion at your end.

If you are unable to resolve this matter – which is of your making – please provide details of how I may make a formal complaint. At this point Amazon have taken £20 for nothing in return.

25/12/16 23:02 from Amazon Customer Services

Hello Andy,

I'm sorry you weren't able to redeem the Gift Card to your account.

Please accept my sincere apologies for any inconvenience caused by this.

If you're able to see the any 3 consecutive digits of the claim code, other than the first 2 or last 4, please reply to this e-mail with these numbers along with the 16 digit card number located on the back of the card.

If you're not able to provide 3 consecutive digits, please reply to this e-mail with the serial number and attach a scanned copy of the card as a PDF, JPG or PNG file.
Please reply to this e-mail with the serial number located on the back of the card and attach a scanned copy of the card as a PDF, JPG or PNG file.

Once received, we'll attempt to validate the card and claim it to the account associated with this e-mail address.

As a representative of Amazon.co.uk, I want to assure you that as our valued customer, your satisfaction is our top priority and be assured that your future order would better reflect our commitment to your satisfaction.​

I highly appreciate your patience, cooperation and understanding in this matter.​
If you need any further information or assistance, please let us know by replying to this e-mail so that we'll be happy to help you further.

We value your business with us and we are looking forward to serve you more in the future.​​​​​​​

25/12/16 23:50 from me

This is a disgrace. Just look how long this email trail is. You are asking for information ALREADY SUPPLIED.

I have ALREADY SENT YOU A PDF of the card which includes the full serial number so why you are asking for that is beyond me. Anyway, I have reattached the PDF. The serial number is ****************, but you can see that in the PDF anyway.

And of course I can see the claim code. We have been trying to enter it into your system to redeem the card. I am pretty sure you are making unreasonable requests now simply to string me along without you actually doing anything. Anyway, in answer to your question about the claim code here are 3 consecutive digits other than the first 2 or last 4: ***. You can check them against the attached PDF if you’ve got nothing better to do. They appear after the first hyphen in the claim code.

And as a reminder, there is no 3rd party retailer. Amazon sold the card.  

Do not reply to me asking for any further information. YOU HAVE IT ALL. Credit my son’s account (omitted@omitted.co.uk) with the monies owed (£20) immediately.

26/12/16 04:45 from Amazon Customer Services

Hello,

I'm sorry to learn about the issue you experienced in relation to the Gift Card. I've reviewed our previous correspondence with you.

The information provided in our last message correctly represents our policy at this time.

As my colleague previously mentioned, I’ve checked your account, and can see that according to our records the Gift Card wasn't activated by the retailer it was purchased from.

Unfortunately, we can't activate this Gift Card for you as it was not created on our system.

The best action to take in this situation is to bring the Gift Card back to the point of purchase with your receipt, where the retailer can reissue a new card for you to use. If you no longer have the receipt, please contact the shop where the card was purchased to resolve this.

Your patience and understanding is greatly appreciated.​

If you still face any issue then I kindly ask you to get in touch via phone. This way, you can speak to our live customer support team who can ensure we resolve this concern to your satisfaction. I'm sorry we don't share account and order information through email address due to security reasons.

I realise that, at this point, asking you to contact us again would be disappointing; however, we really feel that the best way to assist you with this concern is over the phone.

We're available 7 days a week 06.00 to midnight, local UK time. Freephone (within the UK): 0800 496 1081 International customers can reach us at +44 (0) 207 084 7911.

Amazon cares about our customers, and we're working to improve our service and selection.

Your patience and understanding is highly appreciated in this matter.
I hope this helps. We look forward to seeing you again soon.

26/12/16 10:31 from me

Why don’t any of you read the previous emails?

As I said many times, AMAZON IS THE RETAILER.

Anyway, you can read all about it here: http://www.andyfrench.info/2016/12/amazon-gift-cards-how-amazon-can-take.html

Pay the money you owe.

26/12/16 13:30 No email this time – phoned Amazon customer support

I called Amazon Customer Support but they refused to deal with me even though I tried explaining that my son was only 16. They insisted on talking to him direct. Frankly, that’s outrageous.

Anyway, a long call ensued with my son having to read all the numbers on the card several times to the customer services representative and answering many questions.

The outcome? Contact the person who bought the card and ask them to contact Amazon, despite the fact that I told them he’s out of the country on an extended holiday.

Fobbed off again.

26/12/16 16:30 Contacted direct by the Amazon Social Media Team

OK, following a Tweet from me Amazon’s Social Media Team got in touch and sorted the issue out. Success at last and well done the nice Amazon lady who dealt with it. It was nice to talk to someone who could deviate from the script!

Monday, 3 October 2016

Election algorithms for clustered software

The problem

I’ve recently been looking at a problem with some software that was written to work in a cluster. This particular software service runs background jobs against a SQL Server database and in order to support fail-over scenarios the software was written to work in a cluster. Only one service instance (the master) was actually doing any work at any given time with the other instances (the slaves) providing redundancy in the case of a failure. In other words, one instance would be nominated as the master and would take responsibility for running the background jobs. If the master crashed or became unavailable one of the other instances in the cluster would take over as master.

From now on I’m going to continue to use the term service instance to describe a software comonent that participates in a cluster. Each service instance is probably a separate process.

The problem was that the mechanism used to elect and monitor the master was based on UDP broadcast, and broadcast is something that can be problematic in cloud-based environments such as AWS. Given there was a need to migrate this service to the cloud this was a significant issue.

At a high level the election algorithm being used by the cluster was for service instances to use UDP broadcast to exchange messages between themselves to agree which instance would be the master. Once the master had been nominated it took over the work of running the background jobs. The other service instances would then periodically poll the master to check that it was still alive. The first instance to find the master to be unavailable would claim the master role, take over the responsibility of running the jobs and broadcast the change in master.

The use of UDP broadcast in this context was useful because it meant that service instances didn’t need to know about each other. To use more direct addressing it would be necessary to store the addresses of all instances in the cluster in some form of registry or configuration. Configuration management across multiple environments is itself a challenge so reducing the amount of configuration can be an advantage.

However, in this case the use of UDP broadcast was an issue that needed to be addressed to facilitate a move to the cloud. This provided a good opportunity to review clustering election patterns and approaches to writing clustered software in general to see what options are available.

Note: There are alternatives to creating writing software that behave as a cluster natively (e.g. ZooKeeper). This article does not deal with these alternative approaches but focuses on the creation of natively clustered software.

Reasons for clustering

There are typically 2 reasons for writing software that supports clustering:

  • Failover – to prevent outages it would be advantageous to build in redundancy so that if one service instance crashes there’s another available to take up the slack. Note that in this case it isn’t necessary for all instances to be doing useful work. Some may be on stand-by, available to take over if the primary fails but not doing anything while the primary is active.
  • Performance – to facilitate greater application performance running separate software instances (probably on separate servers) may be advantageous. In this case work can be distributed between instances and processed in parallel.

 

Of course, these two aspects are not mutually exclusive; a cluster may support both high availability and distributed processing.

Characteristics of clustered software

Typically when running software as a cluster one instance will be nominated as the coordinator (leader or master). Note that this instance does not have to perform the work itself, it may choose to delegate the work to one of the other instances in the cluster. Alternatively – such as in our example above – the coordinator may perform the work itself exclusively.

This is somewhat analogous to server clustering which can be either symmetrical or asymmetrical. In the symmetrical case every server in the cluster is performing useful work. To distribute work between the servers in the cluster a load balancer is required. In the case of a software cluster it’s the instance elected as the coordinator that’s probably performing this task.

In the asymmetrical case only one server will be active with the other server instances in the cluster being passive. A passive instance will only be activated in the event of a failure of the primary. In the case of a software cluster the coordinator would be the active instance with other instances being passive.

Whichever basic topology is chosen it will be necessary for the software cluster to elect a coordinator when the cluster starts. It will also be necessary for the cluster to recognise when a coordinator has crashed or become unavailable and for this to trigger the election of a new coordinator.

When designing a system like this care should be taken to avoid the coordinator becoming a bottleneck itself. There are also other considerations. For example, in auto-scaling scenarios what happens if the coordinator is shut down as a result of downsizing the infrastructure?

Election patterns

How do software clusters go about managing the election of a coordinator? Below is a discussion of 3 possible approaches:

  • Distributed mutex – use a shared mutex is made available to all service instances and is used to manage which instance is the coordinator. Essentially, all service instances race to grab the mutex. The first to succeed becomes the coordinator.
  • Bully algorithm – use messaging between instances in the cluster to elect the coordinator. The election is based on some unique property of each instance (e.g. a process identifier). The process with the highest value ‘wins’. The winning instance bullies the other instances into submission by keeping the mutex and claiming the coordinator role.
  • Ring algorithm – use messaging between instances in the cluster to elect the coordinator. Service instances are ordered (either physically or logically) so each instance knows its successors. Ordering in the ring is significant with election messages being passed around the ring to figure out which one is ‘at the top’. That instance is elected the coordinator.

 

More detailed descriptions of the approaches are provided below. As you’d expect each has its pros and cons.

Distributed mutex

A mutex “ensures that multiple processes that share resources do not attempt to share the same resource at the same time”. In this case the ‘resource’ is really a role – that of coordinator - that one service instance adopts.

Using a distributed mutex has the advantage that it works in situations where there is no natural leader (e.g. no suitable process identifier which would be required for the Bully Algorithm). Under some circumstances (e.g. when the coordinator is the only instance performing any work) the service instances need not know about each other either; the shared mutex is the only thing an instance needs to know about. In cases where the coordinator needs to distribute work amongst the other instances in the cluster then the coordinator must be able to contact – and therefore know about – the other instances.

The algorithm essentially follows this process:

  1. Service instances race to get a lease over a distributed mutext (e.g. a database object).
  2. The first instance to get the mutex is elected as the coordinator. Other instances are prevented from becoming the coordinator because they are blocked from getting a lease on the mutex.
  3. The coordinator performs the task of coordingating the distribution of work (or executing it itself depending on requirements).
  4. The lease must be set to expire after a period of time and the coordinator must periodically renew the lease. If the coordinator crashes or becomes unavailable it won’t be able to renew the lease on the mutext which will eventually become available again.
  5. All service instances periodically check the mutex to see if the lease has expired. If a service instance finds the lease on the mutex to be available it attempts to secure the lease. If it succeeds the instance becomes the new coordinator.

 

Note that the mutext becomes a potential single point of failure so consideration should be given to a scenario where unavailability of the mutex can prevent the cluster from electing a coordinator.

Another characteristic of using a shared mutex in this way is that election of the leader is non-deterministic. Any service instance in the cluster could take on the role of coordinator.

A good explanation of the shared mutex approach can be found in this article from MSDN.

Bully algorithm

There are some assumptions for the Bully Algorithm:

  • Each instance in the cluster has a unique identifier which must be an ordinal. This could be a process number or even a network address but whatever it is we should be able to order instances in the cluster using this identifier.
  • Each instance knows the identifiers of the other instances that should be participating in the cluster (some may be dead for whatever reason).
  • Service instances don’t know which ones are available and which are not.
  • Service instances must be able to send messages to each other.

 

The basis of the Bully Algorithm is the service instance with the highest identifier will be the coordinator. The algorithm provides a mechanism for service instances to discover which of them has the highest identifier and for that instance to bully the others into submission by claiming the coordinator role. It follows this basic process:

  1. A service instance sends an ELECTION message to all instances with identifiers greater than its own and awaits responses.
  2. If no service instances respond the originator can conclude it has the highest identifier and is therefore safe to assume the role of coordinator. The instance sends a COORDINATOR message to all other instances announcing the fact. Other instances will then start to periodically check that the coordinator is still available. If it isn’t, the instance that finds the coordinator unavailable will start a new election (back to step 1).
  3. Any service instance receiving an ELECTION message and having an identifier greater than the originator will respond with an OK message indicating it’s available.
  4. If in response to an ELECTION message the originator receives an OK response back it knows there’s at least one service instance with a higher identifier than itself. The following then happens:
    1. The original service instance abandons the election (because it knows there’s at least one process with a higher identifier than itself).
    2. Any instances that responded to the ELECTION message with OK now issue ELECTION messages themselves (they start at step 1) and the process repeats until the service with the highest identifier has been elected.

A nice description of the process can be found in this article.

Ring algorithm

As with the Bully Algorithm there are some basic assumptions for the Ring Alorithm.

  • The service instances are ordered in some way.
  • Each service instance uses the ordering to know who its successor is (in fact it needs to know about all the instances in the ring, as we will see below).

 

The Ring Algorithm basically works like this:

  1. All service instances monitor the coordinator.
  2. If any service instance finds the coordinator is not available it sends an ELECTION message to its successor. If the successor is not available the message is sent to the next instance in the ring until an active one is found.
  3. Each service instance that receives the ELECTION message adds its identifier to the message and passes it on as in step 2.
  4. Eventually the message gets back to the originating process instance which recognises the fact because its own identifier is in the list. It examines the list of active instances and finds the one with the highest identifier. The instance then issues a COORDINATOR message informing all the instances in the ring which one is now coordinator (the one with the highest identifier).
  5. The service instance with the highest identifier has now been elected as the coordinator and processing resumes.

 

Note that multiple instances could recognise that the coordinator is unavailable resulting in multiple ELECTION and COORDINATOR messages being sent around the ring. This doesn’t matter, the result is the same.

Other things to look at

A NuGet package is available for a light-weight non-intrusive leader election library for .Net called NanoCluster. Source code is available on GitHub here:

https://github.com/ruslander/NanoCluster

It’s a small project and doesn’t seem to have been used a great deal but might provide some ideas.

References

Friday, 12 August 2016

Problems installing Microsoft .Net Core 1.0.0 VS 2015 Tooling Preview 2

Problem

I had a funky installation of Visual Studio. Everything seemed to be working but when I came to install the Microsoft .Net Core 1.0.0 VS 2015 Tooling it refused to install:

core-001

Repeated attempts to repair or reinstall Visual Studio 2015 Enterprise just wasn’t getting anywhere and I wasn’t in a position to flatten the machine and start again. What was I to do?

Solution

I managed to run the installer and force it not to check Visual Studio using a command line switch:

ukafr02@UKSTOL0079 C:\Users\ukafr02\Downloads
$ DotNetCore.1.0.0-VS2015Tools.Preview2.exe SKIP_VSU_CHECK=1

core-002

core-003

core-004

Happy days!

Friday, 3 June 2016

Enter RSA passphrase once when using Git bash

When using Git bash it can become annoying if you have to enter your RSA passphrase every time you perform a remote operation. You can easily prevent this by using ssh-agent and running it when Git bash first runs.

Open a text editor and create a new text file. Paste the following bash script into the file:

#! /bin/bash 
eval `ssh-agent -s` 
ssh-add ~/.ssh/*_rsa

Note that my RSA key file is called id_rsa and is stored in the .ssh folder in my user’s home directory.

Save the file created above as .bashrc in your user’s home directory. Now, when you start a Git bash session you should be prompted for your passphrase. This will remain active for the duration of the session and you won’t have to enter it again.

conemu

See Saving an SSH key when using Git for info on generating the RSA key file.

Thursday, 2 June 2016

Git recipies

This post is a quick aide-mémoire for basic command-line Git operations. Well worth reading is the Git Getting Started documentation.

Clone a remote repository for the first time

To get started crack open Git bash and go to your source folder. You can then clone a remote repository into a new folder simply by running the following command:

someuser@mymachine MINGW64 ~
$ cd c:

someuser@mymachine MINGW64 /c
$ cd Source/

someuser@mymachine MINGW64 /c/Source
$ git clone git@some:repo/Some.Project.git
Cloning into 'Some.Project'...
Enter passphrase for key '/c/Users/someuser/.ssh/id_rsa':
remote: Counting objects: 1402, done.
remote: Compressing objects: 100% (1311/1311), done.
emote: Total 1402 (delta 1018), reused 87 (delta 59)Receiving objects:  87% (1220/1402), 580.00 KiB | 1.08 MiB/s

Receiving objects: 100% (1402/1402), 1.15 MiB | 1.08 MiB/s, done.
Resolving deltas: 100% (1018/1018), done.
Checking connectivity... done.

someuser@mymachine MINGW64 /c/Source
$                                                            

See the Git - git-clone Documentation.

List branches

The following command lists all local and remote branches:

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/develop
  remotes/origin/feature/IntialLoggingIntegration
  remotes/origin/master

See Git – git-branch Documentation.

To see remote branches only use the –r switch. For local only use the –l branch.

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git branch -r
  origin/HEAD -> origin/master
  origin/develop
  origin/feature/IntialLoggingIntegration
  origin/master

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git branch -l
* master                                                           

Getting more information about the remote repository

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git remote -v
origin  git@some:repo/Some.Project.git (fetch)
origin  git@some:repo/Some.Project.git (push)

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git remote show
origin

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git remote show origin
Enter passphrase for key '/c/Users/someuser/.ssh/id_rsa':
* remote origin
  Fetch URL: git@some:repo/Some.Project.git
  Push  URL: git@some:repo/Some.Project.git
  HEAD branch: master
  Remote branches:
    develop                          tracked
    feature/IntialLoggingIntegration tracked
    master                           tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

Checkout a remote branch

To switch to a new remote branch and check its status:

someuser@mymachine MINGW64 /c/Source/Some.Project (master)
$ git checkout develop
Branch develop set up to track remote branch develop from origin.
Switched to a new branch 'develop'

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git status
On branch develop
Your branch is up-to-date with 'origin/develop'.
nothing to commit, working directory clean

Create a new branch

This example shows how to create a feature branch off develop:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git checkout -b feature/test develop
Switched to a new branch 'feature/test'

See the Git - git-checkout Documentation. Also see the Git Branching - Basic Branching and Merging.

Delete a branch

The following example shows how to delete a branch. Note you can’t be on the branch you’re deleting.

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git branch -D feature/test
Deleted branch feature/test (was 2647fba).

See the Git - git-branch Documentation. Also see the Git Branching - Basic Branching and Merging.

Add all changes

Running ‘git status’ will show you all the changes. If you are happy and want to add them all run:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git add -A

Revert changes to a file

You can see which files have been modified locally using the “git status” command and then undo the changes with “git checkout”:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git status
On branch develop
Your branch is up-to-date with 'origin/develop'.
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   src/Some.Project/App.config
        modified:   src/Some.Other.Project/config/App.config

no changes added to commit (use "git add" and/or "git commit -a")

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git checkout -- src/Some.Project/App.config

Revert all changes

You can see which files have been modified locally using the “git status” command and then undo all the changes with “git reset”:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git status
On branch develop
Your branch is up-to-date with 'origin/develop'.
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   src/Some.Project/App.config
        modified:   src/Some.Other.Project/config/App.config

no changes added to commit (use "git add" and/or "git commit -a")

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git reset –hard

Merge the develop branch into a feature branch

If you are using Git Flow and you are working on a feature branch you might want to merge develop into your feature branch from time to time to minimise conficts once your feature is complete. The basic commands to use would be:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git checkout feature/test
Switched to branch 'feature/test'
Your branch is up-to-date with 'origin/feature/test'.

someuser@mymachine MINGW64 /c/Source/Some.Project (feature/test)
$ git merge develop

See the Git – git-merge Documentation.

Checking to see what might be committed (dry-run)

An easy one - how can you see what will be committed without actually doing so? Like this:

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git commit --dry-run

See the Git – git-commit Documentation.

Delete a local branch

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git branch -d SomeBranchName
Deleted branch SomeBranchName (was 76e05d9).

See the Git – git-branch Documentation.

Move changes to a new branch

If you’ve made changed to a branch but want to move them to a new branch and commit them there do the following (substituting ‘NewBranchNameHere’ with the desired branch name):

someuser@mymachine MINGW64 /c/Source/Some.Project (develop)
$ git checkout -b NewBranchNameHere

Then do git add and git commit as usual.

Thursday, 19 May 2016

Diagnosing log4Net issues in an ASP.Net web application

The Problem

I was trying to get a legacy web application up-and-running on a development workstation but log4Net was not generating any log files. The application was being run from a local instance of IIS 7.5 and built using Visual Studio 2013.

Stepping through the code did not reveal anything relevant and the code to initialise log4Net was being called. So, the problem was how to see what log4Net was doing internally to identify any errors during initialisation.

The Solution

The solution was to enable log4Net’s internal debugging. To do so I modified the web.config file to include a couple of new entries. Firstly, a new appSettings entry:

<appsettings>
    <!-- other settings omitted -->
    <add value="true" key="log4net.Internal.Debug" />
</appsettings>

Then add a new trace listener to system.diagnostics:

<system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add
            name="textWriterTraceListener"
            type="System.Diagnostics.TextWriterTraceListener"
            initializeData="C:\log4net.txt" />
      </listeners>
    </trace>
  </system.diagnostics>

Starting the application now created a log4net.txt file in the root of my C-drive. A quick scan of that log file revealed the issue:

log4net:ERROR [RollingFileAppender] ErrorCode: GenericFailure. Unable to acquire lock on file D:\applog\_root_20160518.0.log. The device is not ready.

As it happens there's no D-drive on my machine! Further investigation revealed that the machine image used to build my workstation included a machine-level web.config file that contained the offending log file location (yes, horrible I know). I changing the entry, restarted the web application and log4Net started working.

References

Monday, 16 May 2016

Creating a SQL Server alias

The Problem

I have a laptop with a SQL Server Express 2012 installed and configured as a named instance (localhost\SQLEXPRESS2012). I’ve cloned a code repository and want to be able to run the applications it contains but they are all configured with connection strings that look for a different instance name (localhost\dev_2012).

I could start modifying connection strings but there are multiple applications and therefore connection strings to modify. I’d prefer to be able to create an alias to the database that matches the one in the configuration files so I don’t need to modify them at all.

The Solution

The solution is to create an alias to the named instance using the SQL Server Configuration Manager.
Open the SQL Server Configuration Manager (Start Menu > All Programs > Microsoft SQL Sever 2012 > Configuration Tools).

Check that TCP/IP is enabled for the instance you are creating an alias for. Enable it if it is not.

sql-alias-001

Once TCP/IP is enabled we can create an alias to the instance for the SQL Native Client. In my case this was for the 32-bit version. Expand the SQL Native Client 11.0 Configuration element and right-click on Aliases. Select New Alias… from the context menu.

Use the new instance name as the Alias Name and set the Server value to the original named instance.

2016-05-16 11_03_01-localhost_dev_2012 Properties

Note the Port No field. By default SQL Server uses 1433 but you can check your setup using the SQL Server Configuration Manager. Open the SQL Server Network Configuration element again and select the protocols for the named instance. Right-click on TCP/IP and view the Properties.

2016-05-16 11_13_10-TCP_IP Properties

If Listen All is enabled on the Protocol tab move to the IP Addresses tab and scroll down to the IPAll section. If Listen All is not enabled you will need to look for the appropriate IP section.

If SQL Server is configured to use a static IP address it will appear in the TCP Port value. If is is configured to use a dynamic port the port in use will appear as the TCP Dynamic Port value. In either case this is the port number to use when creating the alias.

2016-05-16 11_15_22-TCP_IP Properties

Click OK to close any open dialog. Restart SQL Server.

The new instance name will now get routed to the actual instance (calls to localhost\dev_2012 will get routed to localhost\SQLEXPRESS2012 in this case).

You can check everything works by connecting the SQL Server Management Studio to the new instance name.

Tuesday, 12 April 2016

UKMail customer service – FAIL!

We are living the dream. As a family we take full advantage of online retailing but there’s one aspect of the process that seems to be in need of improvement: delivery. 

A company that I’ve had a few unsatisfactory dealings with recently is UKMail. I’ve had disappearing deliveries where either they didn’t try to deliver or – more likely – they did but the driver decided not to leave a card.

Phone system – FAIL

On one such occasion I tried calling by phone and got bounced through the usual impenetrable audio menus until I encountered an option to arrange for a re-delivery. I dutifully pressed 1 as instructed only to be told “Thank you for calling UKMail. Goodbye.” after which I was immediately cut off.

via GIPHY

Happy? No. Really, no.

Contact Us – FAIL

Having tried and failed to use their phone system I decided to raise a complaint using the online Contact Us form. It seems UKMail were ahead of me there and seem to have created a form that you can’t actually submit.

image

Try as I might I couldn’t identify what the erroneous character was. I suspect there’s a proud customer services manager gleefully including in his weekly report the fact that no one is complaining via the website. Now we know why.

I pointed this out on Twitter but I think they missed the point. The following Tweet did result in contact from customer services but only to try and rearrange delivery of the parcel, which I’d already managed to do.

SNAGHTML2f1e3d49

Delivery notification – FAIL

So today, I get home to find yet another UKMail card lying on the door mat but this time annotated by a clearly irritated UKMail delivery man.

image

Loving the “Again”. It didn’t irritate the hell out of me at all.

It might be stating the obvious but if you keep trying to deliver at the same times and there’s never anyone in then you might be trying at the wrong times.

But let’s look a bit closer. This card suggests we’ve been notified in advance in order to give us the change to choose “option 1”. So I checked my email and this is what I found.

SNAGHTML2f266627

This email arrived at 09:42hrs on the day of delivery, only 2 hours ahead of the earliest delivery time given.

What do UKMail expect here? Do they expect us all to be monitoring personal email while at work and filling in online forms to arrange delivery at another time? I’m pretty sure my boss wouldn’t be too happy about that.

UKMail, if you insist on sending these emails at least give us a chance to answer them. Two hours isn’t enough notice. And perhaps point this out to your delivery men so they don’t get snippy on your cards.

Sorry we missed you email – FAIL

This one speaks for itself.

SNAGHTML2f2ef570

Option 1 – FAIL

OK, so the card says quite clearly to visit www.ukmail.com and to select ‘Manage My Delivery’. I did just that, entered the card number and postcode as directed and ended up here.

SNAGHTML2f488989

Can’t see an option 1, 2, 3 or 4 there… Definitely can’t see a “Leave in a safe place option”… Not sure what to do now.

I give up. I think I’ll be looking out for UKMail as a delivery option when making online purchases and selecting something else!

Collect from depot – FAIL

OK, let’s try the Collect from depot option.

SNAGHTML2f667cd5 

Right. No idea what times I can collect then. Not even a default “between 9am and 5pm”. Remember I’m working so what are the chances of them being open after I finish work (i.e. after 5pm)? Nil, I suspect. Do I risk it..?

Well I do as it happens and I end up with this:

SNAGHTML2f6a785d

They don’t like times at UKMail do they - unless it’s giving you 2 hours notice of a delivery.

Conclusion

As a consumer I very often don’t even know which delivery company will deliver any given online purchase. Even if I do, it’s usually the case that I don’t get the chance to specify delivery options such as ‘leave in a safe place’. That seems like a failing to me.

If you leave instructions on a card make sure those instructions can actually be completed by the customer.

Notifications of impending deliveries 2 hours beforehand is a waste of time where people can be expected to be at work.

And why try to deliver during the day at all? Surely most people are out at that time, at least as far as domestic customers are concerned. Wouldn’t evening deliveries by less wasteful in time and resources, not to mention creating better customer relationships?

Online forms that are difficult to submit will aggravate end users, especially when they are already aggravated. Emails that refer to buttons that aren’t there are plain sloppy.

Any individual item given above wouldn’t mean much but when combined result in reduced customer satisfaction and loss of confidence in the service.

Now, let’s try and rearrange delivery of that parcel…

Update – 13/04/2015

After contacting UKMail via Twitter I received the following message:

SNAGHTML16bbb3

So even though I’ve said it’s OK to leave the parcel in a safe place they won’t do it. This illustrates the problem with deliveries and online purchasing. If I as the consumer am not able to specify delivery options like this at the point of sale and if delivery companies won’t allow deliveries outside normal working hours it makes life difficult for all concerned.

It also occurred to me that I hadn’t shown what happens if you try to get your parcel redelivered.

SNAGHTML1cb732

You can’t specify a time of delivery, not even morning or afternoon let alone after 5pm. What are the options here? Only one: take the day off and sit around all day waiting for a delivery. For large or expensive deliveries that might be a viable option but for most of what I do it’s not.

Update – 14/04/2016

As you can see from the screenshots in the Collect from depot – FAIL section above I used the online system to arrange collection from the depot. Imagine my surprise to be sent the following email:

ukmail

As usual the email arrived just 2 hours before the scheduled delivery time (at 10:03hrs actually). So, UKMail have ignored my instruction that I’d be picking the parcel up from the depot and I can expect another crappy card with some suitably irksome message from an irritated delivery driver waiting for me at home.

Just pathetic.

And will the parcel be waiting for me at the depot? Should I waste my time trying to pick it up as I’d arranged?

Update - 15/04/2016

I don’t know why I bothered but I tried collecting the parcel as previously arranged. No surprises here – the result was a big fat “Sorry mate. Bad news I’m afraid…” from the UKMail man in reception. It seems the parcel was still on the van and wouldn’t be back at the depot until 7pm.

So I had to make another journey to collect the parcel after 7pm taking an hour out of my evening. I have the parcel now but that’s it for me. If an online vendor owns up to using UKMail I’m shopping elsewhere.

Saturday, 2 April 2016

Google shows us what user experience shouldn’t look like

I received an email from Google saying that my credit card was expired and asking me add a new one. What ensued was an example of really bad user experience.

Here’s the original email:

SNAGHTML7b873a

There are a few links in the email. Let’s try following the Sign in link. You end up at a web page that looks like this:

image

Now, I don’t think I’ve got a Google for Work account so let’s click the Add account button. I’m asked for sign-in details so I add my usual Google account details and click through.

SNAGHTML6debf4  SNAGHTML6e3fd0 

I click the final Sign in button and end up here:

image

Hang on. That looks familiar. No matter how many times I tried, it was always the same. I went round in a circle and ended up in the same place without an account having been ‘added’.

Back to the email. What happens if I click the Add payment methods link? This does, every time:

image

Wow.

OK, back to first principles. I Google “google for work” and find the http://apps.google.com site. I click Sign in and use the domain I’m administering (this blog) and my usual account details again.

SNAGHTML76c9e9

I click Go and end up here:

image

Hmmm… Looks familiar again but at least we’ve now got a domain mentioned. Can you guess what trying to add an account does? Yes, it does nothing and goes in a circle again.

So, what was the real problem? It seems that because the ‘organisation’ is andyfrench.info I had a separate account in that domain that I had setup to manage the site. I ended up logging in with that account – resetting the password on the way – to get in to the Google at Work admin site. One in it still took me a while to navigate my way to change the credit card details. Whew.

Anyway, it may be secure to not leak any information at all about failed login attempts but really? Is this right?

Monday, 28 March 2016

Files still read-only when refactoring using ReSharper

Problem

ReSharper refactoring operations fail in Visual Studio with the message:

Refactoring Failed

Files still read-only:

Here is an example:

image

This occured with ReSharper Ultimate v10.0.2 and Visual Studio Community Edition 2013 (Version 12.0.300101.00) and the .

Solution

In Visual Studio go to Tools > Options > Source Control > Plug-in Selection and set the source control plug-in to None.

image

Wednesday, 27 January 2016

Webcam not working on Lenovo laptop

Problem

When running Zoom on a Lenovo W540 laptop the webcam was not working despite the indicator light being active. In Zoom the webcam icon looked as if it was disabled. The laptop was running Windows 7.

2016-01-27 15_57_47-Zoom Participant ID_ 31   Meeting ID_ 364-364-797

Solution

Close Zoom or any programs that might use the webcam.

Then go to Control Panel > All Control Panel Items. Open “Lenovo – Web Conferencing”.

2016-01-27 16_04_33-Program Manager

Click “Show my video image”.

2016-01-27 16_06_16-Program Manager

Close the “Lenovo – Web Conferencing” application. The camera should now be working.